<commit_before>use super::{MockAnonymousUser, MockCookieUser, MockTokenUser};
use crate::git::UpstreamIndex;
use crate::record;
use crate::util::{chaosproxy::ChaosProxy, fresh_schema::FreshSchema};
use cargo_registry::config;
use cargo_registry::{
    background_jobs::Environment,
    db::DieselPool,
    git::{Credentials, RepositoryConfig},
    App, Emails,
};
use std::{rc::Rc, sync::Arc, time::Duration};

use cargo_registry::git::{Repository as WorkerRepository, Repository};
use diesel::PgConnection;
use reqwest::{blocking::Client, Proxy};
use std::collections::HashSet;
use swirl::Runner;

struct TestAppInner {
    app: Arc<App>,
    // The bomb (if created) needs to be held in scope until the end of the test.
    _bomb: Option<record::Bomb>,
    middle: conduit_middleware::MiddlewareBuilder,
    index: Option<UpstreamIndex>,
    runner: Option<Runner<Environment, DieselPool>>,
    db_chaosproxy: Option<Arc<ChaosProxy>>,

    // Must be the last field of the struct!
    _fresh_schema: Option<FreshSchema>,
}

impl Drop for TestAppInner {
    fn drop(&mut self) {
        use diesel::prelude::*;
        use swirl::schema::background_jobs::dsl::*;

        // Avoid a double-panic if the test is already failing
        if std::thread::panicking() {
            return;
        }

        // Lazily run any remaining jobs
        if let Some(runner) = &self.runner {
            runner.run_all_pending_jobs().expect("Could not run jobs");
            runner.check_for_failed_jobs().expect("Failed jobs remain");
        }

        // Manually verify that all jobs have completed successfully
        // This will catch any tests that enqueued a job but forgot to initialize the runner
        let conn = self.app.primary_database.get().unwrap();
        let job_count: i64 = background_jobs.count().get_result(&*conn).unwrap();
        assert_eq!(
            0, job_count,
            "Unprocessed or failed jobs remain in the queue"
        );

        // TODO: If a runner was started, obtain the clone from it and ensure its HEAD matches the upstream index HEAD
    }
}

/// A representation of the app and its database transaction
#[derive(Clone)]
pub struct TestApp(Rc<TestAppInner>);

impl TestApp {
    /// Initialize an application with an `Uploader` that panics
    pub fn init() -> TestAppBuilder {
        init_logger();

        TestAppBuilder {
            config: simple_config(),
            proxy: None,
            bomb: None,
            index: None,
            build_job_runner: false,
        }
    }

    /// Initialize the app and a proxy that can record and playback outgoing HTTP requests
    pub fn with_proxy() -> TestAppBuilder {
        Self::init().with_proxy()
    }

    /// Initialize a full application, with a proxy, index, and background worker
    pub fn full() -> TestAppBuilder {
        Self::with_proxy().with_git_index().with_job_runner()
    }

    /// Obtain the database connection and pass it to the closure
    ///
    /// Within each test, the connection pool only has 1 connection so it is necessary to drop the
    /// connection before making any API calls.  Once the closure returns, the connection is
    /// dropped, ensuring it is returned to the pool and available for any future API calls.
    pub fn db<T, F: FnOnce(&PgConnection) -> T>(&self, f: F) -> T {
        let conn = self.0.app.primary_database.get().unwrap();
        f(&conn)
    }

    /// Create a new user with a verified email address in the database and return a mock user
    /// session
    ///
    /// This method updates the database directly
    pub fn db_new_user(&self, username: &str) -> MockCookieUser {
        use cargo_registry::schema::emails;
        use diesel::prelude::*;

        let user = self.db(|conn| {
            let email = "something@example.com";

            let user = crate::new_user(username)
                .create_or_update(None, &self.0.app.emails, conn)
                .unwrap();
            diesel::insert_into(emails::table)
                .values((
                    emails::user_id.eq(user.id),
                    emails::email.eq(email),
                    emails::verified.eq(true),
                ))
                .execute(conn)
                .unwrap();
            user
        });
        MockCookieUser {
            app: self.clone(),
            user,
        }
    }

    /// Obtain a reference to the upstream repository ("the index")
    pub fn upstream_repository(&self) -> &git2::Repository {
        let index = self.0.index.as_ref().unwrap();
        &index.repository
    }

    /// Obtain a list of crates from the index HEAD
    pub fn crates_from_index_head(&self, crate_name: &str) -> Vec<cargo_registry::git::Crate> {
        let path = Repository::relative_index_file(crate_name);
        let index = self.upstream_repository();
        let tree = index.head().unwrap().peel_to_tree().unwrap();
        let blob = tree
            .get_path(&path)
            .unwrap()
            .to_object(index)
            .unwrap()
            .peel_to_blob()
            .unwrap();
        let content = blob.content();

        // The index format consists of one JSON object per line
        // It is not a JSON array
        let lines = std::str::from_utf8(content).unwrap().lines();
        lines
            .map(|line| serde_json::from_str(line).unwrap())
            .collect()
    }

    pub fn run_pending_background_jobs(&self) {
        let runner = &self.0.runner;
        let runner = runner.as_ref().expect("Index has not been initialized");

        runner.run_all_pending_jobs().expect("Could not run jobs");
        runner
            .check_for_failed_jobs()
            .expect("Could not determine if jobs failed");
    }

    /// Obtain a reference to the inner `App` value
    pub fn as_inner(&self) -> &App {
        &self.0.app
    }

    /// Obtain a reference to the inner middleware builder
    pub fn as_middleware(&self) -> &conduit_middleware::MiddlewareBuilder {
        &self.0.middle
    }

    pub(crate) fn db_chaosproxy(&self) -> Arc<ChaosProxy> {
        self.0.db_chaosproxy.clone().expect(
            "ChaosProxy is not enabled on this test, call with_slow_real_pool during app init",
        )
    }
}

pub struct TestAppBuilder {
    config: config::Server,
    proxy: Option<String>,
    bomb: Option<record::Bomb>,
    index: Option<UpstreamIndex>,
    build_job_runner: bool,
}

impl TestAppBuilder {
    /// Create a `TestApp` with an empty database
    pub fn empty(mut self) -> (TestApp, MockAnonymousUser) {
        // Run each test inside a fresh database schema, deleted at the end of the test,
        // The schema will be cleared up once the app is dropped.
        let (db_chaosproxy, fresh_schema) = if !self.config.use_test_database_pool {
            let fresh_schema = FreshSchema::new(&self.config.db.primary.url);
            let (proxy, url) = ChaosProxy::proxy_database_url(fresh_schema.database_url()).unwrap();
            self.config.db.primary.url = url;

            (Some(proxy), Some(fresh_schema))
        } else {
            (None, None)
        };

        let (app, middle) = build_app(self.config, self.proxy);

        let runner = if self.build_job_runner {
            let repository_config = RepositoryConfig {
                index_location: UpstreamIndex::url(),
                credentials: Credentials::Missing,
            };
            let index = WorkerRepository::open(&repository_config).expect("Could not clone index");
            let environment = Environment::new(
                index,
                app.config.uploader().clone(),
                app.http_client().clone(),
            );

            Some(
                Runner::builder(environment)
                    // We only have 1 connection in tests, so trying to run more than
                    // 1 job concurrently will just block
                    .thread_count(1)
                    .connection_pool(app.primary_database.clone())
                    .job_start_timeout(Duration::from_secs(5))
                    .build(),
            )
        } else {
            None
        };

        let test_app_inner = TestAppInner {
            app,
            _fresh_schema: fresh_schema,
            _bomb: self.bomb,
            middle,
            index: self.index,
            runner,
            db_chaosproxy,
        };
        let test_app = TestApp(Rc::new(test_app_inner));
        let anon = MockAnonymousUser {
            app: test_app.clone(),
        };
        (test_app, anon)
    }

    /// Create a proxy for use with this app
    pub fn with_proxy(mut self) -> Self {
        let (proxy, bomb) = record::proxy();
        self.proxy = Some(proxy);
        self.bomb = Some(bomb);
        self
    }

    // Create a `TestApp` with a database including a default user
    pub fn with_user(self) -> (TestApp, MockAnonymousUser, MockCookieUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        (app, anon, user)
    }

    /// Create a `TestApp` with a database including a default user and its token
    pub fn with_token(self) -> (TestApp, MockAnonymousUser, MockCookieUser, MockTokenUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        let token = user.db_new_token("bar");
        (app, anon, user, token)
    }

    pub fn with_config(mut self, f: impl FnOnce(&mut config::Server)) -> Self {
        f(&mut self.config);
        self
    }

    pub fn with_publish_rate_limit(self, rate: Duration, burst: i32) -> Self {
        self.with_config(|config| {
            config.publish_rate_limit.rate = rate;
            config.publish_rate_limit.burst = burst;
        })
    }

    pub fn with_git_index(mut self) -> Self {
        self.index = Some(UpstreamIndex::new().unwrap());
        self
    }

    pub fn with_job_runner(mut self) -> Self {
        self.build_job_runner = true;
        self
    }

    pub fn with_slow_real_db_pool(mut self) -> Self {
        self.config.use_test_database_pool = false;
        self
    }
}

pub fn init_logger() {
    let _ = tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .without_time()
        .with_test_writer()
        .try_init();
}

fn simple_config() -> config::Server {
    config::Server {
        base: config::Base::test(),
        db: config::DatabasePools::test_from_environment(),
        session_key: "test this has to be over 32 bytes long".to_string(),
        gh_client_id: dotenv::var("GH_CLIENT_ID").unwrap_or_default(),
        gh_client_secret: dotenv::var("GH_CLIENT_SECRET").unwrap_or_default(),
        gh_base_url: "http://api.github.com".to_string(),
        max_upload_size: 3000,
        max_unpack_size: 2000,
        publish_rate_limit: Default::default(),
        blocked_traffic: Default::default(),
        max_allowed_page_offset: 200,
        page_offset_ua_blocklist: vec![],
        domain_name: "crates.io".into(),
        allowed_origins: Vec::new(),
        downloads_persist_interval_ms: 1000,
        ownership_invitations_expiration_days: 30,
        metrics_authorization_token: None,
        use_test_database_pool: true,
        instance_metrics_log_every_seconds: None,
        force_unconditional_redirects: false,
        blocked_routes: HashSet::new(),
        version_id_cache_size: 10000,
        version_id_cache_ttl: Duration::from_secs(5 * 60),
    }
}

fn build_app(
    config: config::Server,
    proxy: Option<String>,
) -> (Arc<App>, conduit_middleware::MiddlewareBuilder) {
    let client = if let Some(proxy) = proxy {
        let mut builder = Client::builder();
        builder = builder
            .proxy(Proxy::all(&proxy).expect("Unable to configure proxy with the provided URL"));
        Some(builder.build().expect("TLS backend cannot be initialized"))
    } else {
        None
    };

    let mut app = App::new(config, client);

    // Use the in-memory email backend for all tests, allowing tests to analyze the emails sent by
    // the application. This will also prevent cluttering the filesystem.
    app.emails = Emails::new_in_memory();

    let app = Arc::new(app);
    let handler = cargo_registry::build_handler(Arc::clone(&app));
    (app, handler)
}
<commit_msg>TestApp: Extract `upstream_index()` method<commit_after>use super::{MockAnonymousUser, MockCookieUser, MockTokenUser};
use crate::git::UpstreamIndex;
use crate::record;
use crate::util::{chaosproxy::ChaosProxy, fresh_schema::FreshSchema};
use cargo_registry::config;
use cargo_registry::{
    background_jobs::Environment,
    db::DieselPool,
    git::{Credentials, RepositoryConfig},
    App, Emails,
};
use std::{rc::Rc, sync::Arc, time::Duration};

use cargo_registry::git::{Repository as WorkerRepository, Repository};
use diesel::PgConnection;
use reqwest::{blocking::Client, Proxy};
use std::collections::HashSet;
use swirl::Runner;

struct TestAppInner {
    app: Arc<App>,
    // The bomb (if created) needs to be held in scope until the end of the test.
    _bomb: Option<record::Bomb>,
    middle: conduit_middleware::MiddlewareBuilder,
    index: Option<UpstreamIndex>,
    runner: Option<Runner<Environment, DieselPool>>,
    db_chaosproxy: Option<Arc<ChaosProxy>>,

    // Must be the last field of the struct!
    _fresh_schema: Option<FreshSchema>,
}

impl Drop for TestAppInner {
    fn drop(&mut self) {
        use diesel::prelude::*;
        use swirl::schema::background_jobs::dsl::*;

        // Avoid a double-panic if the test is already failing
        if std::thread::panicking() {
            return;
        }

        // Lazily run any remaining jobs
        if let Some(runner) = &self.runner {
            runner.run_all_pending_jobs().expect("Could not run jobs");
            runner.check_for_failed_jobs().expect("Failed jobs remain");
        }

        // Manually verify that all jobs have completed successfully
        // This will catch any tests that enqueued a job but forgot to initialize the runner
        let conn = self.app.primary_database.get().unwrap();
        let job_count: i64 = background_jobs.count().get_result(&*conn).unwrap();
        assert_eq!(
            0, job_count,
            "Unprocessed or failed jobs remain in the queue"
        );

        // TODO: If a runner was started, obtain the clone from it and ensure its HEAD matches the upstream index HEAD
    }
}

/// A representation of the app and its database transaction
#[derive(Clone)]
pub struct TestApp(Rc<TestAppInner>);

impl TestApp {
    /// Initialize an application with an `Uploader` that panics
    pub fn init() -> TestAppBuilder {
        init_logger();

        TestAppBuilder {
            config: simple_config(),
            proxy: None,
            bomb: None,
            index: None,
            build_job_runner: false,
        }
    }

    /// Initialize the app and a proxy that can record and playback outgoing HTTP requests
    pub fn with_proxy() -> TestAppBuilder {
        Self::init().with_proxy()
    }

    /// Initialize a full application, with a proxy, index, and background worker
    pub fn full() -> TestAppBuilder {
        Self::with_proxy().with_git_index().with_job_runner()
    }

    /// Obtain the database connection and pass it to the closure
    ///
    /// Within each test, the connection pool only has 1 connection so it is necessary to drop the
    /// connection before making any API calls.  Once the closure returns, the connection is
    /// dropped, ensuring it is returned to the pool and available for any future API calls.
    pub fn db<T, F: FnOnce(&PgConnection) -> T>(&self, f: F) -> T {
        let conn = self.0.app.primary_database.get().unwrap();
        f(&conn)
    }

    /// Create a new user with a verified email address in the database and return a mock user
    /// session
    ///
    /// This method updates the database directly
    pub fn db_new_user(&self, username: &str) -> MockCookieUser {
        use cargo_registry::schema::emails;
        use diesel::prelude::*;

        let user = self.db(|conn| {
            let email = "something@example.com";

            let user = crate::new_user(username)
                .create_or_update(None, &self.0.app.emails, conn)
                .unwrap();
            diesel::insert_into(emails::table)
                .values((
                    emails::user_id.eq(user.id),
                    emails::email.eq(email),
                    emails::verified.eq(true),
                ))
                .execute(conn)
                .unwrap();
            user
        });
        MockCookieUser {
            app: self.clone(),
            user,
        }
    }

    /// Obtain a reference to the upstream repository ("the index")
    pub fn upstream_index(&self) -> &UpstreamIndex {
        assert_some!(self.0.index.as_ref())
    }

    /// Obtain a reference to the upstream repository ("the index")
    pub fn upstream_repository(&self) -> &git2::Repository {
        &self.upstream_index().repository
    }

    /// Obtain a list of crates from the index HEAD
    pub fn crates_from_index_head(&self, crate_name: &str) -> Vec<cargo_registry::git::Crate> {
        let path = Repository::relative_index_file(crate_name);
        let index = self.upstream_repository();
        let tree = index.head().unwrap().peel_to_tree().unwrap();
        let blob = tree
            .get_path(&path)
            .unwrap()
            .to_object(index)
            .unwrap()
            .peel_to_blob()
            .unwrap();
        let content = blob.content();

        // The index format consists of one JSON object per line
        // It is not a JSON array
        let lines = std::str::from_utf8(content).unwrap().lines();
        lines
            .map(|line| serde_json::from_str(line).unwrap())
            .collect()
    }

    pub fn run_pending_background_jobs(&self) {
        let runner = &self.0.runner;
        let runner = runner.as_ref().expect("Index has not been initialized");

        runner.run_all_pending_jobs().expect("Could not run jobs");
        runner
            .check_for_failed_jobs()
            .expect("Could not determine if jobs failed");
    }

    /// Obtain a reference to the inner `App` value
    pub fn as_inner(&self) -> &App {
        &self.0.app
    }

    /// Obtain a reference to the inner middleware builder
    pub fn as_middleware(&self) -> &conduit_middleware::MiddlewareBuilder {
        &self.0.middle
    }

    pub(crate) fn db_chaosproxy(&self) -> Arc<ChaosProxy> {
        self.0.db_chaosproxy.clone().expect(
            "ChaosProxy is not enabled on this test, call with_slow_real_pool during app init",
        )
    }
}

pub struct TestAppBuilder {
    config: config::Server,
    proxy: Option<String>,
    bomb: Option<record::Bomb>,
    index: Option<UpstreamIndex>,
    build_job_runner: bool,
}

impl TestAppBuilder {
    /// Create a `TestApp` with an empty database
    pub fn empty(mut self) -> (TestApp, MockAnonymousUser) {
        // Run each test inside a fresh database schema, deleted at the end of the test,
        // The schema will be cleared up once the app is dropped.
        let (db_chaosproxy, fresh_schema) = if !self.config.use_test_database_pool {
            let fresh_schema = FreshSchema::new(&self.config.db.primary.url);
            let (proxy, url) = ChaosProxy::proxy_database_url(fresh_schema.database_url()).unwrap();
            self.config.db.primary.url = url;

            (Some(proxy), Some(fresh_schema))
        } else {
            (None, None)
        };

        let (app, middle) = build_app(self.config, self.proxy);

        let runner = if self.build_job_runner {
            let repository_config = RepositoryConfig {
                index_location: UpstreamIndex::url(),
                credentials: Credentials::Missing,
            };
            let index = WorkerRepository::open(&repository_config).expect("Could not clone index");
            let environment = Environment::new(
                index,
                app.config.uploader().clone(),
                app.http_client().clone(),
            );

            Some(
                Runner::builder(environment)
                    // We only have 1 connection in tests, so trying to run more than
                    // 1 job concurrently will just block
                    .thread_count(1)
                    .connection_pool(app.primary_database.clone())
                    .job_start_timeout(Duration::from_secs(5))
                    .build(),
            )
        } else {
            None
        };

        let test_app_inner = TestAppInner {
            app,
            _fresh_schema: fresh_schema,
            _bomb: self.bomb,
            middle,
            index: self.index,
            runner,
            db_chaosproxy,
        };
        let test_app = TestApp(Rc::new(test_app_inner));
        let anon = MockAnonymousUser {
            app: test_app.clone(),
        };
        (test_app, anon)
    }

    /// Create a proxy for use with this app
    pub fn with_proxy(mut self) -> Self {
        let (proxy, bomb) = record::proxy();
        self.proxy = Some(proxy);
        self.bomb = Some(bomb);
        self
    }

    // Create a `TestApp` with a database including a default user
    pub fn with_user(self) -> (TestApp, MockAnonymousUser, MockCookieUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        (app, anon, user)
    }

    /// Create a `TestApp` with a database including a default user and its token
    pub fn with_token(self) -> (TestApp, MockAnonymousUser, MockCookieUser, MockTokenUser) {
        let (app, anon) = self.empty();
        let user = app.db_new_user("foo");
        let token = user.db_new_token("bar");
        (app, anon, user, token)
    }

    pub fn with_config(mut self, f: impl FnOnce(&mut config::Server)) -> Self {
        f(&mut self.config);
        self
    }

    pub fn with_publish_rate_limit(self, rate: Duration, burst: i32) -> Self {
        self.with_config(|config| {
            config.publish_rate_limit.rate = rate;
            config.publish_rate_limit.burst = burst;
        })
    }

    pub fn with_git_index(mut self) -> Self {
        self.index = Some(UpstreamIndex::new().unwrap());
        self
    }

    pub fn with_job_runner(mut self) -> Self {
        self.build_job_runner = true;
        self
    }

    pub fn with_slow_real_db_pool(mut self) -> Self {
        self.config.use_test_database_pool = false;
        self
    }
}

pub fn init_logger() {
    let _ = tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .without_time()
        .with_test_writer()
        .try_init();
}

fn simple_config() -> config::Server {
    config::Server {
        base: config::Base::test(),
        db: config::DatabasePools::test_from_environment(),
        session_key: "test this has to be over 32 bytes long".to_string(),
        gh_client_id: dotenv::var("GH_CLIENT_ID").unwrap_or_default(),
        gh_client_secret: dotenv::var("GH_CLIENT_SECRET").unwrap_or_default(),
        gh_base_url: "http://api.github.com".to_string(),
        max_upload_size: 3000,
        max_unpack_size: 2000,
        publish_rate_limit: Default::default(),
        blocked_traffic: Default::default(),
        max_allowed_page_offset: 200,
        page_offset_ua_blocklist: vec![],
        domain_name: "crates.io".into(),
        allowed_origins: Vec::new(),
        downloads_persist_interval_ms: 1000,
        ownership_invitations_expiration_days: 30,
        metrics_authorization_token: None,
        use_test_database_pool: true,
        instance_metrics_log_every_seconds: None,
        force_unconditional_redirects: false,
        blocked_routes: HashSet::new(),
        version_id_cache_size: 10000,
        version_id_cache_ttl: Duration::from_secs(5 * 60),
    }
}

fn build_app(
    config: config::Server,
    proxy: Option<String>,
) -> (Arc<App>, conduit_middleware::MiddlewareBuilder) {
    let client = if let Some(proxy) = proxy {
        let mut builder = Client::builder();
        builder = builder
            .proxy(Proxy::all(&proxy).expect("Unable to configure proxy with the provided URL"));
        Some(builder.build().expect("TLS backend cannot be initialized"))
    } else {
        None
    };

    let mut app = App::new(config, client);

    // Use the in-memory email backend for all tests, allowing tests to analyze the emails sent by
    // the application. This will also prevent cluttering the filesystem.
    app.emails = Emails::new_in_memory();

    let app = Arc::new(app);
    let handler = cargo_registry::build_handler(Arc::clone(&app));
    (app, handler)
}
<|endoftext|>


<commit_before>//! "Weighted" graph types

use std;
use std::ops::{Deref,DerefMut};
use std::fmt::Debug;

use smallvec::SmallVec;

use super::interface::{self, Id, Graph, Node as INode, Edge as IEdge};
use super::common::{IndexType,NodeIndex,EdgeIndex};

/// Trait bounds for types used as edge and node data payloads.
pub trait Data: Clone + Debug {}
impl<T> Data for T where T: Clone + Debug {}

// ----------------------------------------------------------------
// Edge

/// Weighted edge type.
#[derive(Clone,Debug)]
pub struct Edge<T: Data, I: Id> {
    source: I,
    target: I,
    data: T
}

impl<T, I> interface::Edge for Edge<T, I>
    where T: Data, I: Id {
    impl_basic_edge!(I);
}

impl<T: Data, I: Id> Edge<T, I> {
    /// Create an edge with the given source & target node indices and
    /// weight data.
    pub fn new(source: I, target: I, data: T) -> Self {
        Edge{source: source, target: target, data: data}
    }
}

impl<T: Data, I: Id> Deref for Edge<T, I> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T: Data, I: Id> DerefMut for Edge<T, I> {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.data
    }
}

impl<T: Data, I: Id, I2: Copy> From<(I2, I2)> for Edge<T, I>
    where T: Default, I: From<I2> {
    fn from(u: (I2, I2)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), Default::default())
    }
}

impl<T: Data, I: Id, I2: Copy> From<(I2, I2, T)> for Edge<T, I>
    where I: From<I2> {
    fn from(u: (I2, I2, T)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), u.2)
    }
}

impl<'a, T: Data, I: Id, I2: Copy> From<&'a (I2, I2)> for Edge<T, I>
    where T: Default, I: From<I2> {
    fn from(u: &'a (I2, I2)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), Default::default())
    }
}
impl<'a, T: Data, I: Id, I2: Copy> From<&'a (I2, I2, T)> for Edge<T, I>
    where T: Clone, I: From<I2> {
    fn from(u: &'a (I2, I2, T)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), u.2.clone())
    }
}

// ----------------------------------------------------------------
// Node

///  Weighted node implementation.
///
/// A reference to the node's weight data can be obtained using the type's
/// `Deref` implementation.
///
/// ```rust
/// use cripes::util::graph::{EdgeIndex,WeightedNode};
///
/// # fn main() {
/// let n = WeightedNode::<_, EdgeIndex<u8>>::new(32);
/// assert_eq!(32, *n);
/// # }
/// ```

#[derive(Clone,Debug)]
pub struct Node<T: Data, I: Id> {
    incoming_edges: SmallVec<[I; 8]>,
    outgoing_edges: SmallVec<[I; 8]>,
    data: T
}

impl<T: Data, I: Id> Node<T, I> {
    /// Instantiate a node with the given data.
    pub fn new(data: T) -> Self {
        Node{incoming_edges: SmallVec::new(), outgoing_edges: SmallVec::new(), data: data}
    }
}

impl<T: Data, I: Id> Deref for Node<T, I> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T: Data, I: Id> DerefMut for Node<T, I> {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.data
    }
}

impl<T, I> interface::Node for Node<T, I>
        where T: Data, I: Id {
    impl_basic_node!(I);
}

// ----------------------------------------------------------------
// AdjacencyList

impl<N: Data, E: Data, Ix: IndexType> super::common::AdjacencyList<Node<N, EdgeIndex<Ix>>, Edge<E, NodeIndex<Ix>>> {
    /// Add a node to the graph.
    ///
    /// @return Index of the newly-added node.
    pub fn add_node(&mut self, data: N) -> NodeIndex<Ix> {
        <Self as interface::Graph>::add_node(self, /*<Self as interface::Graph>::*/Node::new(data))
    }

    /// Add an edge to the graph.
    ///
    /// The source and target nodes, as identified by calling `edge.source()`
    /// and `edge.target()` on the edge, will be updated as appropriate.
    ///
    /// **Panics** if either of the source or target node indices are invalid.
    ///
    /// @return Index of the newly-added edge.
    ///
    /// @fixme Is there any way we can make this operation atomic, or at least
    /// less drawn-out?
    #[inline(always)]
    pub fn add_edge(&mut self, source: NodeIndex<Ix>, target: NodeIndex<Ix>, data: E) -> EdgeIndex<Ix> {

        // Add the edge itself
        <Self as interface::Graph>::add_edge(self, Edge::new(source, target, data))
    }
}
<commit_msg>util/graph/weighted.rs: remove some unused imports<commit_after>//! "Weighted" graph types

use std;
use std::ops::{Deref,DerefMut};
use std::fmt::Debug;

use smallvec::SmallVec;

use super::interface::{self, Id};
use super::common::{IndexType,NodeIndex,EdgeIndex};

/// Trait bounds for types used as edge and node data payloads.
pub trait Data: Clone + Debug {}
impl<T> Data for T where T: Clone + Debug {}

// ----------------------------------------------------------------
// Edge

/// Weighted edge type.
#[derive(Clone,Debug)]
pub struct Edge<T: Data, I: Id> {
    source: I,
    target: I,
    data: T
}

impl<T, I> interface::Edge for Edge<T, I>
    where T: Data, I: Id {
    impl_basic_edge!(I);
}

impl<T: Data, I: Id> Edge<T, I> {
    /// Create an edge with the given source & target node indices and
    /// weight data.
    pub fn new(source: I, target: I, data: T) -> Self {
        Edge{source: source, target: target, data: data}
    }
}

impl<T: Data, I: Id> Deref for Edge<T, I> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T: Data, I: Id> DerefMut for Edge<T, I> {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.data
    }
}

impl<T: Data, I: Id, I2: Copy> From<(I2, I2)> for Edge<T, I>
    where T: Default, I: From<I2> {
    fn from(u: (I2, I2)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), Default::default())
    }
}

impl<T: Data, I: Id, I2: Copy> From<(I2, I2, T)> for Edge<T, I>
    where I: From<I2> {
    fn from(u: (I2, I2, T)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), u.2)
    }
}

impl<'a, T: Data, I: Id, I2: Copy> From<&'a (I2, I2)> for Edge<T, I>
    where T: Default, I: From<I2> {
    fn from(u: &'a (I2, I2)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), Default::default())
    }
}
impl<'a, T: Data, I: Id, I2: Copy> From<&'a (I2, I2, T)> for Edge<T, I>
    where T: Clone, I: From<I2> {
    fn from(u: &'a (I2, I2, T)) -> Self {
        Self::new(I::from(u.0), I::from(u.1), u.2.clone())
    }
}

// ----------------------------------------------------------------
// Node

///  Weighted node implementation.
///
/// A reference to the node's weight data can be obtained using the type's
/// `Deref` implementation.
///
/// ```rust
/// use cripes::util::graph::{EdgeIndex,WeightedNode};
///
/// # fn main() {
/// let n = WeightedNode::<_, EdgeIndex<u8>>::new(32);
/// assert_eq!(32, *n);
/// # }
/// ```

#[derive(Clone,Debug)]
pub struct Node<T: Data, I: Id> {
    incoming_edges: SmallVec<[I; 8]>,
    outgoing_edges: SmallVec<[I; 8]>,
    data: T
}

impl<T: Data, I: Id> Node<T, I> {
    /// Instantiate a node with the given data.
    pub fn new(data: T) -> Self {
        Node{incoming_edges: SmallVec::new(), outgoing_edges: SmallVec::new(), data: data}
    }
}

impl<T: Data, I: Id> Deref for Node<T, I> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T: Data, I: Id> DerefMut for Node<T, I> {
    fn deref_mut(&mut self) -> &mut <Self as Deref>::Target {
        &mut self.data
    }
}

impl<T, I> interface::Node for Node<T, I>
        where T: Data, I: Id {
    impl_basic_node!(I);
}

// ----------------------------------------------------------------
// AdjacencyList

impl<N: Data, E: Data, Ix: IndexType> super::common::AdjacencyList<Node<N, EdgeIndex<Ix>>, Edge<E, NodeIndex<Ix>>> {
    /// Add a node to the graph.
    ///
    /// @return Index of the newly-added node.
    pub fn add_node(&mut self, data: N) -> NodeIndex<Ix> {
        <Self as interface::Graph>::add_node(self, /*<Self as interface::Graph>::*/Node::new(data))
    }

    /// Add an edge to the graph.
    ///
    /// The source and target nodes, as identified by calling `edge.source()`
    /// and `edge.target()` on the edge, will be updated as appropriate.
    ///
    /// **Panics** if either of the source or target node indices are invalid.
    ///
    /// @return Index of the newly-added edge.
    ///
    /// @fixme Is there any way we can make this operation atomic, or at least
    /// less drawn-out?
    #[inline(always)]
    pub fn add_edge(&mut self, source: NodeIndex<Ix>, target: NodeIndex<Ix>, data: E) -> EdgeIndex<Ix> {

        // Add the edge itself
        <Self as interface::Graph>::add_edge(self, Edge::new(source, target, data))
    }
}
<|endoftext|>


<commit_before>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

use frontend::lexer::Token;
use backend::zcode::zfile;
use backend::zcode::zfile::{FormattingState, ZOP};
use std::collections::HashMap;

//==============================
// ast

pub struct AST {
    passages: Vec<ASTNode>
}

 /// add zcode based on tokens
fn gen_zcode<'a>(node: &'a ASTNode, mut out: &mut zfile::Zfile, mut manager: &mut CodeGenManager<'a>) -> Vec<ZOP> {
    let mut state_copy = manager.format_state.clone();
    let mut set_formatting = false;

    match node {
        &ASTNode::Passage(ref node) => {
            let mut code: Vec<ZOP> = vec![];
            match &node.category {
                &Token::TokPassageName(ref name) => {
                    code.push(ZOP::Routine{name: name.to_string(), count_variables: 0});
                },
                _ => {
                    debug!("no match 1");
                }
            };

            for child in &node.childs {
                for instr in gen_zcode(child, out, manager) {
                    code.push(instr);
                }

            }

            code.push(ZOP::Newline);
            code.push(ZOP::Call{jump_to_label: "system_check_links".to_string()});
            code
        },
        &ASTNode::Default(ref t) => {
            let mut code: Vec<ZOP> = match &t.category {
                &Token::TokText(ref s) => {
                    vec![ZOP::PrintOps{text: s.to_string()}]
                },
                &Token::TokNewLine => {
                    vec![ZOP::Newline]
                },
                &Token::TokFormatBoldStart => {
                    state_copy.bold = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokFormatItalicStart => {
                    state_copy.italic = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokPassageLink (ref name, ref link) => {
                    set_formatting = true;
                    vec![
                    ZOP::CallWithAddress{jump_to_label: "system_add_link".to_string(), address: link.to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: true, monospace: state_copy.mono, italic: state_copy.italic},
                    ZOP::Print{text: format!("{}[", name)},
                    ZOP::PrintNumVar{variable: 16},
                    ZOP::Print{text: "]".to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}
                    ]
                },
                &Token::TokAssign(ref var, ref operator) => {
                    if operator == "=" || operator == "to" {
                        if !manager.symbol_table.is_known_symbol(var) {
                            manager.symbol_table.insert_new_symbol(&var);
                        }
                        let symbol_id = manager.symbol_table.get_symbol_id(var);
                        if t.childs.len() == 1 {
                            match t.childs[0].as_default().category {
                                Token::TokInt(value) => {
                                    vec![ZOP::StoreU16{variable: symbol_id, value: value as u16}]
                                },
                                Token::TokBoolean(ref bool_val) => {
                                    vec![ZOP::StoreU8{variable: symbol_id, value: boolstr_to_u8(&*bool_val)}]
                                }
                                _ => { vec![] }
                            }
                        } else {
                            debug!("Assign Expression currently not supported.");
                            vec![]
                        }
                    } else { vec![] }
                },
                &Token::TokIf => {
                    if t.childs.len() < 2 {
                        panic!("Unsupported if-expression!");
                    }

                    let mut compare: u8 = 1;

                    // check if the first node is a pseudonode
                    let pseudo_node = match t.childs[0].as_default().category {
                        Token::TokPseudo => t.childs[0].as_default(),
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    // Check if first token is variable
                    let var_name = match pseudo_node.childs[0].as_default().category {
                        Token::TokVariable(ref var) => var,
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    if pseudo_node.childs.len() > 1 {
                        // Check if second token is compare operator
                        match pseudo_node.childs[1].as_default().category {
                            Token::TokCompOp(ref op) => {
                                match &*(*op) {
                                    "==" | "is" => {} ,
                                    _ => panic!("Unsupported Compare Operator!")
                                }
                            }, _ =>  panic!("Unsupported if-expression!")
                        }

                        // Check if third token is number
                        compare = match pseudo_node.childs[2].as_default().category {
                            Token::TokInt(ref value) => {
                                *value as u8
                            },
                            Token::TokBoolean(ref bool_val) => {
                                boolstr_to_u8(&*bool_val)
                            }, _ => panic!("Unsupported assign value!")
                        };
                    }

                    let symbol_id = manager.symbol_table.get_symbol_id(&*var_name);
                    let if_id = manager.ids_if.start_next();

                    let if_label = format!("if_{}", if_id);
                    let after_if_label = format!("after_if_{}", if_id);
                    let after_else_label = format!("after_else_{}", if_id);
                    let mut code: Vec<ZOP> = vec![
                        ZOP::JE{local_var_id: symbol_id, equal_to_const: compare, jump_to_label: if_label.to_string()},
                        ZOP::Jump{jump_to_label: after_if_label.to_string()},
                        ZOP::Label{name: if_label.to_string()}
                    ];

                    for i in 1..t.childs.len() {
                        for instr in gen_zcode(&t.childs[i], out, manager) {
                            code.push(instr);
                        }
                    }

                    code.push(ZOP::Jump{jump_to_label: after_else_label});
                    code.push(ZOP::Label{name: after_if_label});
                    code
                },
                &Token::TokElse => {
                    let mut code: Vec<ZOP> = vec![];
                    for child in &t.childs {
                        for instr in gen_zcode(child, out, manager) {
                            code.push(instr);
                        }
                    }
                    code
                },
                &Token::TokEndIf => {
                    let after_else_label = format!("after_else_{}", manager.ids_if.pop_id());
                    vec![ZOP::Label{name: after_else_label}]
                },
                &Token::TokMakroVar(ref name) => {
                    let var_id = manager.symbol_table.get_symbol_id(&*name);
                    println!("{:?}", var_id);
                    vec![ZOP::PrintNumVar{variable: var_id}]
                },
                _ => {
                    debug!("no match if");
                    vec![]
                }
            };
            if set_formatting {
                for child in &t.childs {
                    for instr in gen_zcode(child, out, manager) {
                        code.push(instr);
                    }
                }
                code.push(ZOP::SetTextStyle{bold: false, reverse: false, monospace: false, italic: false});
                let state = manager.format_state;
                code.push(ZOP::SetTextStyle{bold: state.bold, reverse: state.inverted, monospace: state.mono, italic: state.italic});
            }
            code
        }
    }

}

fn boolstr_to_u8(string: &str) -> u8 {
    match string {
        "true" => 1 as u8,
        _ => 0 as u8
    }
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(& self, out: &mut zfile::Zfile) {
        let mut manager = CodeGenManager::new();
        let mut code: Vec<ZOP> = vec![];
        for child in &self.passages {
            for instr in gen_zcode(child, out, &mut manager) {
                code.push(instr);
            }
        }
        debug!("emit zcode:");
        for instr in &code {
            debug!("{:?}", instr);
        }
        out.emit(code);
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].count_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

struct CodeGenManager<'a> {
    ids_if: IdentifierProvider,
    symbol_table: SymbolTable<'a>,
    format_state: FormattingState
}

struct IdentifierProvider {
    current_id: u32,
    id_stack: Vec<u32>
}

struct SymbolTable<'a> {
    current_id: u8,
    symbol_map: HashMap<&'a str, u8>
}

impl <'a> CodeGenManager<'a> {
    pub fn new() -> CodeGenManager<'a> {
        CodeGenManager {
            ids_if: IdentifierProvider::new(),
            symbol_table: SymbolTable::new(),
            format_state: FormattingState {bold: false, italic: false, mono: false, inverted: false}
        }
    }
}

impl IdentifierProvider {
    pub fn new() -> IdentifierProvider {
        IdentifierProvider {
            current_id: 0,
            id_stack: Vec::new()
        }
    }

    // Returns a new id and pushes it onto the stack
    pub fn start_next(&mut self) -> u32 {
        let id = self.current_id;
        self.current_id += 1;
        self.id_stack.push(id);
        id
    }

    // Pops the last id from the stack
    pub fn pop_id(&mut self) -> u32 {
        self.id_stack.pop().unwrap()
    }
}

impl <'a> SymbolTable<'a> {
    pub fn new() -> SymbolTable<'a> {
        SymbolTable {
            current_id: 25,
            symbol_map: HashMap::<&str, u8>::new()
        }
    }

    // Inserts a symbol into the table, assigning a new id
    pub fn insert_new_symbol(&mut self, symbol: &'a str) {
        debug!("Assigned id {} to variable {}", self.current_id, symbol);
        self.symbol_map.insert(symbol, self.current_id);
        self.current_id += 1;
    }

    // Checks if the symbol is already existent in the table
    pub fn is_known_symbol(&self, symbol: &str) -> bool {
        self.symbol_map.contains_key(symbol)
    }

    // Returns the id for a given symbol
    // (check if is_known_symbol, otherwise panics)
    pub fn get_symbol_id(&self, symbol: &str) -> u8 {
        *self.symbol_map.get(symbol).unwrap()
    }
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].count_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].count_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }

    pub fn as_default(&self) -> &NodeDefault {
        match self {
            &ASTNode::Default(ref def) => def,
            _ => panic!("Node cannot be unwrapped as NodeDefault!")
        }
    }
}
<commit_msg>variables have now types in the symboltable<commit_after>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

use frontend::lexer::Token;
use backend::zcode::zfile;
use backend::zcode::zfile::{FormattingState, ZOP};
use std::collections::HashMap;

//==============================
// ast
#[derive(Clone)]
enum Type{
    Bool,
    Integer,
    String,
}

pub struct AST {
    passages: Vec<ASTNode>
}

 /// add zcode based on tokens
fn gen_zcode<'a>(node: &'a ASTNode, mut out: &mut zfile::Zfile, mut manager: &mut CodeGenManager<'a>) -> Vec<ZOP> {
    let mut state_copy = manager.format_state.clone();
    let mut set_formatting = false;

    match node {
        &ASTNode::Passage(ref node) => {
            let mut code: Vec<ZOP> = vec![];
            match &node.category {
                &Token::TokPassageName(ref name) => {
                    code.push(ZOP::Routine{name: name.to_string(), count_variables: 0});
                },
                _ => {
                    debug!("no match 1");
                }
            };

            for child in &node.childs {
                for instr in gen_zcode(child, out, manager) {
                    code.push(instr);
                }

            }

            code.push(ZOP::Newline);
            code.push(ZOP::Call{jump_to_label: "system_check_links".to_string()});
            code
        },
        &ASTNode::Default(ref t) => {
            let mut code: Vec<ZOP> = match &t.category {
                &Token::TokText(ref s) => {
                    vec![ZOP::PrintOps{text: s.to_string()}]
                },
                &Token::TokNewLine => {
                    vec![ZOP::Newline]
                },
                &Token::TokFormatBoldStart => {
                    state_copy.bold = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokFormatItalicStart => {
                    state_copy.italic = true;
                    set_formatting = true;
                    vec![ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}]
                },
                &Token::TokPassageLink (ref name, ref link) => {
                    set_formatting = true;
                    vec![
                    ZOP::CallWithAddress{jump_to_label: "system_add_link".to_string(), address: link.to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: true, monospace: state_copy.mono, italic: state_copy.italic},
                    ZOP::Print{text: format!("{}[", name)},
                    ZOP::PrintNumVar{variable: 16},
                    ZOP::Print{text: "]".to_string()},
                    ZOP::SetTextStyle{bold: state_copy.bold, reverse: state_copy.inverted, monospace: state_copy.mono, italic: state_copy.italic}
                    ]
                },
                &Token::TokAssign(ref var, ref operator) => {
                    if operator == "=" || operator == "to" {
                        if !manager.symbol_table.is_known_symbol(var) {
                            manager.symbol_table.insert_new_symbol(&var, Type::Integer);
                        }
                        let symbol_id = manager.symbol_table.get_symbol_id(var);
                        if t.childs.len() == 1 {
                            match t.childs[0].as_default().category {
                                Token::TokInt(value) => {
                                    vec![ZOP::StoreU16{variable: symbol_id, value: value as u16}]
                                },
                                Token::TokBoolean(ref bool_val) => {
                                    vec![ZOP::StoreU8{variable: symbol_id, value: boolstr_to_u8(&*bool_val)}]
                                }
                                _ => { vec![] }
                            }
                        } else {
                            debug!("Assign Expression currently not supported.");
                            vec![]
                        }
                    } else { vec![] }
                },
                &Token::TokIf => {
                    if t.childs.len() < 2 {
                        panic!("Unsupported if-expression!");
                    }

                    let mut compare: u8 = 1;

                    // check if the first node is a pseudonode
                    let pseudo_node = match t.childs[0].as_default().category {
                        Token::TokPseudo => t.childs[0].as_default(),
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    // Check if first token is variable
                    let var_name = match pseudo_node.childs[0].as_default().category {
                        Token::TokVariable(ref var) => var,
                        _ =>  panic!("Unsupported if-expression!")
                    };

                    if pseudo_node.childs.len() > 1 {
                        // Check if second token is compare operator
                        match pseudo_node.childs[1].as_default().category {
                            Token::TokCompOp(ref op) => {
                                match &*(*op) {
                                    "==" | "is" => {} ,
                                    _ => panic!("Unsupported Compare Operator!")
                                }
                            }, _ =>  panic!("Unsupported if-expression!")
                        }

                        // Check if third token is number
                        compare = match pseudo_node.childs[2].as_default().category {
                            Token::TokInt(ref value) => {
                                *value as u8
                            },
                            Token::TokBoolean(ref bool_val) => {
                                boolstr_to_u8(&*bool_val)
                            }, _ => panic!("Unsupported assign value!")
                        };
                    }

                    let symbol_id = manager.symbol_table.get_symbol_id(&*var_name);
                    let if_id = manager.ids_if.start_next();

                    let if_label = format!("if_{}", if_id);
                    let after_if_label = format!("after_if_{}", if_id);
                    let after_else_label = format!("after_else_{}", if_id);
                    let mut code: Vec<ZOP> = vec![
                        ZOP::JE{local_var_id: symbol_id, equal_to_const: compare, jump_to_label: if_label.to_string()},
                        ZOP::Jump{jump_to_label: after_if_label.to_string()},
                        ZOP::Label{name: if_label.to_string()}
                    ];

                    for i in 1..t.childs.len() {
                        for instr in gen_zcode(&t.childs[i], out, manager) {
                            code.push(instr);
                        }
                    }

                    code.push(ZOP::Jump{jump_to_label: after_else_label});
                    code.push(ZOP::Label{name: after_if_label});
                    code
                },
                &Token::TokElse => {
                    let mut code: Vec<ZOP> = vec![];
                    for child in &t.childs {
                        for instr in gen_zcode(child, out, manager) {
                            code.push(instr);
                        }
                    }
                    code
                },
                &Token::TokEndIf => {
                    let after_else_label = format!("after_else_{}", manager.ids_if.pop_id());
                    vec![ZOP::Label{name: after_else_label}]
                },
                &Token::TokMakroVar(ref name) => {
                    let var_id = manager.symbol_table.get_symbol_id(&*name);
                    match manager.symbol_table.get_symbol_type(&*name) {
                        Type::Integer => {
                            vec![ZOP::PrintNumVar{variable: var_id}]
                        },
                        Type::String => {
                            vec![]
                        },
                        Type::Bool => {
                            vec![]
                        }
                    }
                },
                _ => {
                    debug!("no match if");
                    vec![]
                }
            };
            if set_formatting {
                for child in &t.childs {
                    for instr in gen_zcode(child, out, manager) {
                        code.push(instr);
                    }
                }
                code.push(ZOP::SetTextStyle{bold: false, reverse: false, monospace: false, italic: false});
                let state = manager.format_state;
                code.push(ZOP::SetTextStyle{bold: state.bold, reverse: state.inverted, monospace: state.mono, italic: state.italic});
            }
            code
        }
    }

}

fn boolstr_to_u8(string: &str) -> u8 {
    match string {
        "true" => 1 as u8,
        _ => 0 as u8
    }
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(& self, out: &mut zfile::Zfile) {
        let mut manager = CodeGenManager::new();
        let mut code: Vec<ZOP> = vec![];
        for child in &self.passages {
            for instr in gen_zcode(child, out, &mut manager) {
                code.push(instr);
            }
        }
        debug!("emit zcode:");
        for instr in &code {
            debug!("{:?}", instr);
        }
        out.emit(code);
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].count_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

struct CodeGenManager<'a> {
    ids_if: IdentifierProvider,
    symbol_table: SymbolTable<'a>,
    format_state: FormattingState
}

struct IdentifierProvider {
    current_id: u32,
    id_stack: Vec<u32>
}

struct SymbolTable<'a> {
    current_id: u8,
    symbol_map: HashMap<&'a str, (u8, Type)>
}

impl <'a> CodeGenManager<'a> {
    pub fn new() -> CodeGenManager<'a> {
        CodeGenManager {
            ids_if: IdentifierProvider::new(),
            symbol_table: SymbolTable::new(),
            format_state: FormattingState {bold: false, italic: false, mono: false, inverted: false}
        }
    }
}

impl IdentifierProvider {
    pub fn new() -> IdentifierProvider {
        IdentifierProvider {
            current_id: 0,
            id_stack: Vec::new()
        }
    }

    // Returns a new id and pushes it onto the stack
    pub fn start_next(&mut self) -> u32 {
        let id = self.current_id;
        self.current_id += 1;
        self.id_stack.push(id);
        id
    }

    // Pops the last id from the stack
    pub fn pop_id(&mut self) -> u32 {
        self.id_stack.pop().unwrap()
    }
}

impl <'a> SymbolTable<'a> {
    pub fn new() -> SymbolTable<'a> {
        SymbolTable {
            current_id: 25,
            symbol_map: HashMap::<&str, (u8,Type)>::new()
        }
    }

    // Inserts a symbol into the table, assigning a new id
    pub fn insert_new_symbol(&mut self, symbol: &'a str, t: Type) {
        debug!("Assigned id {} to variable {}", self.current_id, symbol);
        self.symbol_map.insert(symbol, (self.current_id,t));
        self.current_id += 1;
    }

    // Checks if the symbol is already existent in the table
    pub fn is_known_symbol(&self, symbol: &str) -> bool {
        self.symbol_map.contains_key(symbol)
    }

    // Returns the id for a given symbol
    // (check if is_known_symbol, otherwise panics)
    pub fn get_symbol_id(&self, symbol: &str) -> u8 {
        let (b,_) = self.symbol_map.get(symbol).unwrap().clone();
        b
    }

    pub fn get_symbol_type(&self, symbol: &str) -> Type {
        let (_,b) = self.symbol_map.get(symbol).unwrap().clone();
        let mut t:Type;
        t = b.clone();
        t
    }
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn count_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].count_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].count_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }

    pub fn as_default(&self) -> &NodeDefault {
        match self {
            &ASTNode::Default(ref def) => def,
            _ => panic!("Node cannot be unwrapped as NodeDefault!")
        }
    }
}
<|endoftext|>


<commit_before>#![feature(convert, plugin, custom_attribute)]
#![plugin(jit_macros)]
#[no_link] #[macro_use]
extern crate jit_macros;
extern crate jit;
use jit::*;

#[test]
fn test_context_tags() {
    let mut ctx = Context::<isize>::new();
    ctx[0] = 3;
    ctx[1] = 33;
    assert_eq!(ctx[0], 3);
    assert_eq!(ctx[1], 33);
}
#[test]
#[should_panic]
fn test_context_bad_tags() {
    let mut ctx = Context::<isize>::new();
    ctx[0] = 3;
    ctx[1] = 33;
    assert_eq!(ctx[2], 3);
}

#[derive(Debug, Eq, PartialEq)]
struct PanicDrop(isize);
impl Drop for PanicDrop {
    fn drop(&mut self) {
        panic!("Dropped {:?}", self)
    }
}
#[test]
#[should_panic]
fn test_panic_tags() {
    let pos_t = get::<Pos>();
    let kind = pos_t.get_kind();
    let pos_t = TaggedType::new(&pos_t, kind, Box::new(PanicDrop(42)));
    assert_eq!(pos_t.get_tagged_data(), Some(&PanicDrop(42)));
}
#[jit]
#[repr(packed)]
struct Pos {
    x: f64,
    y: f64
}
#[test]
fn test_tags() {
    let pos_t = get::<Pos>();
    let kind = pos_t.get_kind();
    let new_pos_t = TaggedType::new(&pos_t, kind, Box::new(42));
    assert!(new_pos_t.get_tagged_data() == Some(&42));
    assert!(new_pos_t.get_tagged_type() == &*pos_t);
}
<commit_msg>Remove old feature convert from tags unit test<commit_after>#![feature(plugin, custom_attribute)]
#![plugin(jit_macros)]
#[no_link] #[macro_use]
extern crate jit_macros;
extern crate jit;
use jit::*;

#[test]
fn test_context_tags() {
    let mut ctx = Context::<isize>::new();
    ctx[0] = 3;
    ctx[1] = 33;
    assert_eq!(ctx[0], 3);
    assert_eq!(ctx[1], 33);
}
#[test]
#[should_panic]
fn test_context_bad_tags() {
    let mut ctx = Context::<isize>::new();
    ctx[0] = 3;
    ctx[1] = 33;
    assert_eq!(ctx[2], 3);
}

#[derive(Debug, Eq, PartialEq)]
struct PanicDrop(isize);
impl Drop for PanicDrop {
    fn drop(&mut self) {
        panic!("Dropped {:?}", self)
    }
}
#[test]
#[should_panic]
fn test_panic_tags() {
    let pos_t = get::<Pos>();
    let kind = pos_t.get_kind();
    let pos_t = TaggedType::new(&pos_t, kind, Box::new(PanicDrop(42)));
    assert_eq!(pos_t.get_tagged_data(), Some(&PanicDrop(42)));
}
#[jit]
#[repr(packed)]
struct Pos {
    x: f64,
    y: f64
}
#[test]
fn test_tags() {
    let pos_t = get::<Pos>();
    let kind = pos_t.get_kind();
    let new_pos_t = TaggedType::new(&pos_t, kind, Box::new(42));
    assert!(new_pos_t.get_tagged_data() == Some(&42));
    assert!(new_pos_t.get_tagged_type() == &*pos_t);
}
<|endoftext|>


<commit_before>extern mod std;
extern mod pcre;

use pcre::*;

#[cfg(test)]
mod test_util {
    pub trait OptionUtil<T> {
        fn is_some_and(blk: &fn(T) -> bool) -> bool;
        fn is_none_and(blk: &fn() -> bool) -> bool;
    }

    impl<T: Copy> OptionUtil<T> for Option<T> {
        fn is_some_and(blk: &fn(T) -> bool) -> bool {
            match self {
                Some(t) => blk(t),
                None => false,
            }
        }

        // Who wants?
        fn is_none_and(blk: &fn() -> bool) -> bool {
            match self {
                Some(_) => false,
                None => blk(),
            }
        }
    }

    pub trait ResultUtil<T, U> {
        fn is_ok_and(blk: &fn(T) -> bool) -> bool;
        fn is_err_and(blk: &fn(U) -> bool) -> bool;
    }

    impl<T: Copy, U: Copy> ResultUtil<T, U> for Result<T, U> {
        fn is_ok_and(blk: &fn(T) -> bool) -> bool {
            match self {
                Ok(t) => blk(t),
                Err(_) => false,
            }
        }

        fn is_err_and(blk: &fn(U) -> bool) -> bool {
            match self {
                Ok(_) => false,
                Err(u) => blk(u),
            }
        }
    }

    #[test]
    fn test_option_util() {
        let s = Some(42);

        assert!( s.is_some());
        assert!( s.is_some_and(|i| i == 42));
        assert!(!s.is_some_and(|i| i != 42));

        assert!(!s.is_none());
        assert!(!s.is_none_and(|| true));
        assert!(!s.is_none_and(|| false));

        let n = None::<()>;

        assert!( n.is_none());
        assert!( n.is_none_and(|| true));
        assert!(!n.is_none_and(|| false));

        assert!(!n.is_some());
        assert!(!n.is_some_and(|_nil| true));
        assert!(!n.is_some_and(|_nil| false));

    }

    #[test]
    fn test_result_util() {
        let o: Result<int, ()> = Ok(42);

        assert!( o.is_ok());
        assert!( o.is_ok_and(|i| i == 42));
        assert!(!o.is_ok_and(|i| i != 42));

        assert!(!o.is_err());
        assert!(!o.is_err_and(|_nil| true));
        assert!(!o.is_err_and(|_nil| false));

        let e: Result<(), int> = Err(42);

        assert!( e.is_err());
        assert!( e.is_err_and(|i| i == 42));
        assert!(!e.is_err_and(|i| i != 42));

        assert!(!e.is_ok());
        assert!(!e.is_ok_and(|_nil| true));
        assert!(!e.is_ok_and(|_nil| false));
    }
}

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

    #[test]
    fn test_compile() {
        let r = compile("foo", 0);
        assert!(r.is_ok());

        let r = compile("foo(", 0);
        assert!(do r.is_err_and |e| {
            assert!(e.code == 14);
            assert!(e.reason == @~"missing )");
            assert!(e.offset == 4u);
            true
        });
    }

    #[test]
    fn test_search() {
        let r = search("(foo)bar", "foobar", 0);
        assert!(r.is_ok());

        let r = search(~"(foo)bar", "foobar", 0);
        assert!(r.is_ok());

        let r = search(@"(foo)bar", "foobar", 0);
        assert!(r.is_ok());

        let c = compile("(foo)bar", 0);
        let p = c.get();

        let r = search(p, "foobar", 0);
        assert!(r.is_ok());

        let r = search(c, "foobar", 0);
        assert!(r.is_ok());

        let r = search("foo(", "foobar", 0);
        match r {
            Err(CompileErr(e)) => {
                assert!(e.code == 14);
                assert!(e.reason == @~"missing )");
                assert!(e.offset == 4u);
            }
            _ => { fail!(); }
        }

        let r = search("(foo)bar", "baz", 0);
        assert!(is_nomatch(r));

        let r = search("はにほ", "いろはにほへと", 0);
        assert!(r.is_ok_and(|m| m.begin() == 6u && m.end() == 15u));

        let r = search("ちりぬ", "いろはにほへと", 0);
        assert!(is_nomatch(r));
    }

    // compile() accepts compile options (obviously)
    #[test]
    fn test_search_options_0() {
        let r = search(compile("foobar", PCRE_CASELESS), "FOOBAR", 0);
        assert!(r.is_ok());
    }

    // search() accepts compile options
    #[test]
    fn test_search_options_1() {
        let r = search("foobar", "FOOBAR", PCRE_CASELESS);
        assert!(r.is_ok());
    }

    // Inline options supersedes exec-time compile options
    #[test]
    fn test_search_options_2() {
        let r = search("(?-i)foobar", "FOOBAR", PCRE_CASELESS);
        assert!(r.is_err());
    }

    // Compile-time compile options supersedes exec-time compile options
    #[test]
    fn test_search_options_3() {
        let r = search(compile("foobar", 0), "FOOBAR", PCRE_CASELESS);
        assert!(r.is_err());
    }

    #[test]
    fn test_replace() {
        let r = replace("bcd", "AbcdbcdbcdE", "BCD", 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDbcdbcdE"));
    }

    #[test]
    fn test_replace_from() {
        let r = replace_from("bcd", "AbcdbcdbcdE", "BCD", 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDbcdE"));
    }

    #[test]
    fn test_replace_fn() {
        let r = replace_fn("bcd", "AbcdbcdbcdE",
                           |m| { str::to_upper(m.matched()) }, 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDbcdbcdE"));
    }

    #[test]
    fn test_replace_fn_from() {
        let r = replace_fn_from("bcd", "AbcdbcdbcdE",
                                |m| { str::to_upper(m.matched()) }, 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDbcdE"));
    }

    #[test]
    fn test_replace_all() {
        let r = replace_all("bcd", "AbcdbcdbcdE", "BCD", 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDBCDBCDE"));
    }

    #[test]
    fn test_replace_all_from() {
        let r = replace_all_from("bcd", "AbcdbcdbcdE", "BCD", 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDBCDE"));
    }

    #[test]
    fn test_replace_all_fn() {
        let r = replace_all_fn("bcd", "AbcdbcdbcdE",
                                |m| { str::to_upper(m.matched()) }, 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDBCDBCDE"));
    }

    #[test]
    fn test_replace_all_fn_from() {
        let r = replace_all_fn_from("bcd", "AbcdbcdbcdE",
                                    |m| { str::to_upper(m.matched()) }, 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDBCDE"));
    }
}

#[cfg(test)]
mod test_match_extensions {
    use test_util::*;
    use pcre::*;

    #[test]
    fn test_group() {
        let r = search("(foo)bar(baz)", "foobarbaz", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.group(0u).is_some_and(|s| s == @~"foobarbaz"));
            assert!(m.group(1u).is_some_and(|s| s == @~"foo"));
            assert!(m.group(2u).is_some_and(|s| s == @~"baz"));
            assert!(m.group(3u).is_none());
            true
        });
    }

    #[test]
    fn test_subgroups() {
        let r = search("(foo)bar(baz)", "foobarbaz", 0);
        assert!(do r.is_ok_and |m| {
            do vec::all2(m.subgroups(), ~[~"foo", ~"baz"]) |s, t| { s == t }
        });
    }

    #[test]
    fn test_group_count() {
        let r = search("foobarbaz", "foobarbaz", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 0u));

        let r = search("(foo)bar(baz)", "foobarbaz", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 2u));

        let r = search("(?:foo)bar", "foobar", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 0u));

        let r = search("(?:(foo)|baz)bar", "foobar", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 1u));

        let r = search("(?:foo|(baz))bar", "foobar", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 1u));
    }

    #[test]
    fn test_unmatched_group() {
        let r = search("((foo)|bar)_(baz)", "bar_baz", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.group(1u).is_some_and(|s| s == @~"bar"));
            assert!(m.group(2u).is_none());
            assert!(m.group(3u).is_some_and(|s| s == @~"baz"));
            true
        });

        let r = search("(foo)?(bar)(baz)", "barbaz", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.group(1u).is_none());
            assert!(m.group(2u).is_some_and(|s| s == @~"bar"));
            assert!(m.group(3u).is_some_and(|s| s == @~"baz"));
            true
        });

        let r = search("(foo)?(?<bar_name>bar)", "bar", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.named_group("bar_name").is_some_and(|s| s == @~"bar"));
            true
        });
    }

    #[test]
    fn test_group_names() {
        let r = search("(?<foo_name>foo)bar", "foobar", 0);
        assert!(do r.is_ok_and |m| {
            do vec::all2(m.group_names(), ~[~"foo_name"]) |s, t| { s == t }
        });
    }

    #[test]
    fn test_named_group() {
        let r = search("(?<foo_name>f..)bar", "foobar", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.named_group("foo_name").is_some_and(|s| s == @~"foo"));
            assert!(m.named_group("nonexistent").is_none());
            true
        });
    }
}
<commit_msg>Explicit `self` parameter<commit_after>extern mod std;
extern mod pcre;

use pcre::*;

#[cfg(test)]
mod test_util {
    pub trait OptionUtil<T> {
        fn is_some_and(&self, blk: &fn(T) -> bool) -> bool;
        fn is_none_and(&self, blk: &fn() -> bool) -> bool;
    }

    impl<T: Copy> OptionUtil<T> for Option<T> {
        fn is_some_and(&self, blk: &fn(T) -> bool) -> bool {
            match *self {
                Some(t) => blk(t),
                None => false,
            }
        }

        // Who wants?
        fn is_none_and(&self, blk: &fn() -> bool) -> bool {
            match *self {
                Some(_) => false,
                None => blk(),
            }
        }
    }

    pub trait ResultUtil<T, U> {
        fn is_ok_and(&self, blk: &fn(T) -> bool) -> bool;
        fn is_err_and(&self, blk: &fn(U) -> bool) -> bool;
    }

    impl<T: Copy, U: Copy> ResultUtil<T, U> for Result<T, U> {
        fn is_ok_and(&self, blk: &fn(T) -> bool) -> bool {
            match *self {
                Ok(t) => blk(t),
                Err(_) => false,
            }
        }

        fn is_err_and(&self, blk: &fn(U) -> bool) -> bool {
            match *self {
                Ok(_) => false,
                Err(u) => blk(u),
            }
        }
    }

    #[test]
    fn test_option_util() {
        let s = Some(42);

        assert!( s.is_some());
        assert!( s.is_some_and(|i| i == 42));
        assert!(!s.is_some_and(|i| i != 42));

        assert!(!s.is_none());
        assert!(!s.is_none_and(|| true));
        assert!(!s.is_none_and(|| false));

        let n = None::<()>;

        assert!( n.is_none());
        assert!( n.is_none_and(|| true));
        assert!(!n.is_none_and(|| false));

        assert!(!n.is_some());
        assert!(!n.is_some_and(|_nil| true));
        assert!(!n.is_some_and(|_nil| false));

    }

    #[test]
    fn test_result_util() {
        let o: Result<int, ()> = Ok(42);

        assert!( o.is_ok());
        assert!( o.is_ok_and(|i| i == 42));
        assert!(!o.is_ok_and(|i| i != 42));

        assert!(!o.is_err());
        assert!(!o.is_err_and(|_nil| true));
        assert!(!o.is_err_and(|_nil| false));

        let e: Result<(), int> = Err(42);

        assert!( e.is_err());
        assert!( e.is_err_and(|i| i == 42));
        assert!(!e.is_err_and(|i| i != 42));

        assert!(!e.is_ok());
        assert!(!e.is_ok_and(|_nil| true));
        assert!(!e.is_ok_and(|_nil| false));
    }
}

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

    #[test]
    fn test_compile() {
        let r = compile("foo", 0);
        assert!(r.is_ok());

        let r = compile("foo(", 0);
        assert!(do r.is_err_and |e| {
            assert!(e.code == 14);
            assert!(e.reason == @~"missing )");
            assert!(e.offset == 4u);
            true
        });
    }

    #[test]
    fn test_search() {
        let r = search("(foo)bar", "foobar", 0);
        assert!(r.is_ok());

        let r = search(~"(foo)bar", "foobar", 0);
        assert!(r.is_ok());

        let r = search(@"(foo)bar", "foobar", 0);
        assert!(r.is_ok());

        let c = compile("(foo)bar", 0);
        let p = c.get();

        let r = search(p, "foobar", 0);
        assert!(r.is_ok());

        let r = search(c, "foobar", 0);
        assert!(r.is_ok());

        let r = search("foo(", "foobar", 0);
        match r {
            Err(CompileErr(e)) => {
                assert!(e.code == 14);
                assert!(e.reason == @~"missing )");
                assert!(e.offset == 4u);
            }
            _ => { fail!(); }
        }

        let r = search("(foo)bar", "baz", 0);
        assert!(is_nomatch(r));

        let r = search("はにほ", "いろはにほへと", 0);
        assert!(r.is_ok_and(|m| m.begin() == 6u && m.end() == 15u));

        let r = search("ちりぬ", "いろはにほへと", 0);
        assert!(is_nomatch(r));
    }

    // compile() accepts compile options (obviously)
    #[test]
    fn test_search_options_0() {
        let r = search(compile("foobar", PCRE_CASELESS), "FOOBAR", 0);
        assert!(r.is_ok());
    }

    // search() accepts compile options
    #[test]
    fn test_search_options_1() {
        let r = search("foobar", "FOOBAR", PCRE_CASELESS);
        assert!(r.is_ok());
    }

    // Inline options supersedes exec-time compile options
    #[test]
    fn test_search_options_2() {
        let r = search("(?-i)foobar", "FOOBAR", PCRE_CASELESS);
        assert!(r.is_err());
    }

    // Compile-time compile options supersedes exec-time compile options
    #[test]
    fn test_search_options_3() {
        let r = search(compile("foobar", 0), "FOOBAR", PCRE_CASELESS);
        assert!(r.is_err());
    }

    #[test]
    fn test_replace() {
        let r = replace("bcd", "AbcdbcdbcdE", "BCD", 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDbcdbcdE"));
    }

    #[test]
    fn test_replace_from() {
        let r = replace_from("bcd", "AbcdbcdbcdE", "BCD", 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDbcdE"));
    }

    #[test]
    fn test_replace_fn() {
        let r = replace_fn("bcd", "AbcdbcdbcdE",
                           |m| { str::to_upper(m.matched()) }, 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDbcdbcdE"));
    }

    #[test]
    fn test_replace_fn_from() {
        let r = replace_fn_from("bcd", "AbcdbcdbcdE",
                                |m| { str::to_upper(m.matched()) }, 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDbcdE"));
    }

    #[test]
    fn test_replace_all() {
        let r = replace_all("bcd", "AbcdbcdbcdE", "BCD", 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDBCDBCDE"));
    }

    #[test]
    fn test_replace_all_from() {
        let r = replace_all_from("bcd", "AbcdbcdbcdE", "BCD", 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDBCDE"));
    }

    #[test]
    fn test_replace_all_fn() {
        let r = replace_all_fn("bcd", "AbcdbcdbcdE",
                                |m| { str::to_upper(m.matched()) }, 0);
        assert!(r.is_ok_and(|s| s == @~"ABCDBCDBCDE"));
    }

    #[test]
    fn test_replace_all_fn_from() {
        let r = replace_all_fn_from("bcd", "AbcdbcdbcdE",
                                    |m| { str::to_upper(m.matched()) }, 2u, 0);
        assert!(r.is_ok_and(|s| s == @~"AbcdBCDBCDE"));
    }
}

#[cfg(test)]
mod test_match_extensions {
    use test_util::*;
    use pcre::*;

    #[test]
    fn test_group() {
        let r = search("(foo)bar(baz)", "foobarbaz", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.group(0u).is_some_and(|s| s == @~"foobarbaz"));
            assert!(m.group(1u).is_some_and(|s| s == @~"foo"));
            assert!(m.group(2u).is_some_and(|s| s == @~"baz"));
            assert!(m.group(3u).is_none());
            true
        });
    }

    #[test]
    fn test_subgroups() {
        let r = search("(foo)bar(baz)", "foobarbaz", 0);
        assert!(do r.is_ok_and |m| {
            do vec::all2(m.subgroups(), ~[~"foo", ~"baz"]) |s, t| { s == t }
        });
    }

    #[test]
    fn test_group_count() {
        let r = search("foobarbaz", "foobarbaz", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 0u));

        let r = search("(foo)bar(baz)", "foobarbaz", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 2u));

        let r = search("(?:foo)bar", "foobar", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 0u));

        let r = search("(?:(foo)|baz)bar", "foobar", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 1u));

        let r = search("(?:foo|(baz))bar", "foobar", 0);
        assert!(r.is_ok_and(|m| m.group_count() == 1u));
    }

    #[test]
    fn test_unmatched_group() {
        let r = search("((foo)|bar)_(baz)", "bar_baz", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.group(1u).is_some_and(|s| s == @~"bar"));
            assert!(m.group(2u).is_none());
            assert!(m.group(3u).is_some_and(|s| s == @~"baz"));
            true
        });

        let r = search("(foo)?(bar)(baz)", "barbaz", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.group(1u).is_none());
            assert!(m.group(2u).is_some_and(|s| s == @~"bar"));
            assert!(m.group(3u).is_some_and(|s| s == @~"baz"));
            true
        });

        let r = search("(foo)?(?<bar_name>bar)", "bar", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.named_group("bar_name").is_some_and(|s| s == @~"bar"));
            true
        });
    }

    #[test]
    fn test_group_names() {
        let r = search("(?<foo_name>foo)bar", "foobar", 0);
        assert!(do r.is_ok_and |m| {
            do vec::all2(m.group_names(), ~[~"foo_name"]) |s, t| { s == t }
        });
    }

    #[test]
    fn test_named_group() {
        let r = search("(?<foo_name>f..)bar", "foobar", 0);
        assert!(do r.is_ok_and |m| {
            assert!(m.named_group("foo_name").is_some_and(|s| s == @~"foo"));
            assert!(m.named_group("nonexistent").is_none());
            true
        });
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Mac OS-specific implementation of cross-process surfaces. This uses `IOSurface`, introduced
//! in Mac OS X 10.6 Snow Leopard.

use core_foundation::boolean::CFBoolean;
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::CFNumber;
use core_foundation::string::CFString;
use geom::size::Size2D;
use io_surface::{kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow, kIOSurfaceHeight};
use io_surface::{kIOSurfaceIsGlobal, kIOSurfaceWidth, IOSurface};
use io_surface;
use opengles::cgl::CGLPixelFormatObj;
use platform::surface::NativeSurfaceMethods;
use std::util;
use texturegl::Texture;

pub struct NativeGraphicsMetadata {
    pixel_format: CGLPixelFormatObj,
}

pub struct NativePaintingGraphicsContext {
    metadata: NativeGraphicsMetadata,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        NativePaintingGraphicsContext {
            metadata: *metadata,
        }
    }
}

impl Drop for NativePaintingGraphicsContext {
    fn drop(&mut self) {}
}

pub struct NativeCompositingGraphicsContext {
    contents: (),
}

impl NativeCompositingGraphicsContext {
    pub fn new() -> NativeCompositingGraphicsContext {
        NativeCompositingGraphicsContext {
            contents: (),
        }
    }
}

pub struct NativeSurface {
    io_surface: Option<IOSurface>,
    will_leak: bool,
}

impl NativeSurface {
    #[fixed_stack_segment]
    pub fn from_io_surface(io_surface: IOSurface) -> NativeSurface {
        NativeSurface {
            io_surface: Some(io_surface),
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(_: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32) -> NativeSurface {
        let width_key = CFString::wrap_shared(kIOSurfaceWidth);
        let width_value = CFNumber::new(size.width);

        let height_key = CFString::wrap_shared(kIOSurfaceHeight);
        let height_value = CFNumber::new(size.height);

        let bytes_per_row_key = CFString::wrap_shared(kIOSurfaceBytesPerRow);
        let bytes_per_row_value = CFNumber::new(stride);

        let bytes_per_elem_key = CFString::wrap_shared(kIOSurfaceBytesPerElement);
        let bytes_per_elem_value = CFNumber::new(4i32);

        let is_global_key = CFString::wrap_shared(kIOSurfaceIsGlobal);
        let is_global_value = CFBoolean::true_value();

        let surface = io_surface::new(&CFDictionary::new([
            (*width_key.contents.borrow_ref(), *width_value.contents.borrow_type_ref()),
            (*height_key.contents.borrow_ref(), *height_value.contents.borrow_type_ref()),
            (*bytes_per_row_key.contents.borrow_ref(),
             *bytes_per_row_value.contents.borrow_type_ref()),
            (*bytes_per_elem_key.contents.borrow_ref(),
             *bytes_per_elem_value.contents.borrow_type_ref()),
            (*is_global_key.contents.borrow_ref(), *is_global_value.contents.borrow_type_ref()),
        ]));

        NativeSurface {
            io_surface: Some(surface),
            will_leak: true,
        }
    }

    fn bind_to_texture(&self,
                       _: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       size: Size2D<int>) {
        let _bound_texture = texture.bind();
        self.io_surface.get_ref().bind_to_gl_texture(size)
    }

    fn upload(&self, _: &NativePaintingGraphicsContext, data: &[u8]) {
        self.io_surface.get_ref().upload(data)
    }

    fn get_id(&self) -> int {
        match self.io_surface {
            None => 0,
            Some(ref io_surface) => io_surface.get_id() as int,
        }
    }

    fn destroy(&mut self, _: &NativePaintingGraphicsContext) {
        let _ = util::replace(&mut self.io_surface, None);
        self.mark_wont_leak()
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<commit_msg>Update for Core Foundation bindings revamp<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Mac OS-specific implementation of cross-process surfaces. This uses `IOSurface`, introduced
//! in Mac OS X 10.6 Snow Leopard.

use core_foundation::base::TCFType;
use core_foundation::boolean::CFBoolean;
use core_foundation::dictionary::CFDictionary;
use core_foundation::number::CFNumber;
use core_foundation::string::CFString;
use geom::size::Size2D;
use io_surface::{kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow, kIOSurfaceHeight};
use io_surface::{kIOSurfaceIsGlobal, kIOSurfaceWidth, IOSurface};
use io_surface;
use opengles::cgl::CGLPixelFormatObj;
use platform::surface::NativeSurfaceMethods;
use std::util;
use texturegl::Texture;

pub struct NativeGraphicsMetadata {
    pixel_format: CGLPixelFormatObj,
}

pub struct NativePaintingGraphicsContext {
    metadata: NativeGraphicsMetadata,
}

impl NativePaintingGraphicsContext {
    pub fn from_metadata(metadata: &NativeGraphicsMetadata) -> NativePaintingGraphicsContext {
        NativePaintingGraphicsContext {
            metadata: *metadata,
        }
    }
}

impl Drop for NativePaintingGraphicsContext {
    fn drop(&mut self) {}
}

pub struct NativeCompositingGraphicsContext {
    contents: (),
}

impl NativeCompositingGraphicsContext {
    pub fn new() -> NativeCompositingGraphicsContext {
        NativeCompositingGraphicsContext {
            contents: (),
        }
    }
}

pub struct NativeSurface {
    io_surface: Option<IOSurface>,
    will_leak: bool,
}

impl NativeSurface {
    #[fixed_stack_segment]
    pub fn from_io_surface(io_surface: IOSurface) -> NativeSurface {
        NativeSurface {
            io_surface: Some(io_surface),
            will_leak: true,
        }
    }
}

impl NativeSurfaceMethods for NativeSurface {
    fn new(_: &NativePaintingGraphicsContext, size: Size2D<i32>, stride: i32) -> NativeSurface {
        unsafe {
            let width_key: CFString = TCFType::wrap_under_get_rule(kIOSurfaceWidth);
            let width_value: CFNumber = FromPrimitive::from_i32(size.width).unwrap();

            let height_key: CFString = TCFType::wrap_under_get_rule(kIOSurfaceHeight);
            let height_value: CFNumber = FromPrimitive::from_i32(size.height).unwrap();

            let bytes_per_row_key: CFString = TCFType::wrap_under_get_rule(kIOSurfaceBytesPerRow);
            let bytes_per_row_value: CFNumber = FromPrimitive::from_i32(stride).unwrap();

            let bytes_per_elem_key: CFString =
                TCFType::wrap_under_get_rule(kIOSurfaceBytesPerElement);
            let bytes_per_elem_value: CFNumber = FromPrimitive::from_i32(4).unwrap();

            let is_global_key: CFString = TCFType::wrap_under_get_rule(kIOSurfaceIsGlobal);
            let is_global_value = CFBoolean::true_value();

            let surface = io_surface::new(&CFDictionary::from_CFType_pairs([
                (width_key.as_CFType(), width_value.as_CFType()),
                (height_key.as_CFType(), height_value.as_CFType()),
                (bytes_per_row_key.as_CFType(), bytes_per_row_value.as_CFType()),
                (bytes_per_elem_key.as_CFType(), bytes_per_elem_value.as_CFType()),
                (is_global_key.as_CFType(), is_global_value.as_CFType()),
            ]));

            NativeSurface {
                io_surface: Some(surface),
                will_leak: true,
            }
        }
    }

    fn bind_to_texture(&self,
                       _: &NativeCompositingGraphicsContext,
                       texture: &Texture,
                       size: Size2D<int>) {
        let _bound_texture = texture.bind();
        self.io_surface.get_ref().bind_to_gl_texture(size)
    }

    fn upload(&self, _: &NativePaintingGraphicsContext, data: &[u8]) {
        self.io_surface.get_ref().upload(data)
    }

    fn get_id(&self) -> int {
        match self.io_surface {
            None => 0,
            Some(ref io_surface) => io_surface.get_id() as int,
        }
    }

    fn destroy(&mut self, _: &NativePaintingGraphicsContext) {
        let _ = util::replace(&mut self.io_surface, None);
        self.mark_wont_leak()
    }

    fn mark_will_leak(&mut self) {
        self.will_leak = true
    }

    fn mark_wont_leak(&mut self) {
        self.will_leak = false
    }
}

<|endoftext|>