<commit_before>//! Utility module for fetching key attributes from a DICOM object.

use dicom_core::DataDictionary;
use dicom_object::{mem::InMemElement, FileDicomObject, InMemDicomObject};
use snafu::{Backtrace, ResultExt, Snafu};

#[derive(Debug, Snafu)]
pub enum GetAttributeError {
    #[snafu(display("Missing required attribute `{}`", name))]
    MissingRequiredField {
        name: &'static str,
        #[snafu(backtrace)]
        source: dicom_object::Error,
    },

    #[snafu(display("Could not convert attribute `{}`", name))]
    CastValue {
        name: &'static str,
        source: dicom_core::value::CastValueError,
        backtrace: Backtrace,
    },
}

pub type Result<T, E = GetAttributeError> = std::result::Result<T, E>;

/// Get the Columns from the DICOM object
pub fn cols<D: DataDictionary + Clone>(obj: &FileDicomObject<InMemDicomObject<D>>) -> Result<u16> {
    obj.element(dicom_dictionary_std::tags::COLUMNS)
        .context(MissingRequiredFieldSnafu { name: "Columns" })?
        .uint16()
        .context(CastValueSnafu { name: "Columns" })
}

/// Get the Rows from the DICOM object
pub fn rows<D: DataDictionary + Clone>(obj: &FileDicomObject<InMemDicomObject<D>>) -> Result<u16> {
    obj.element(dicom_dictionary_std::tags::ROWS)
        .context(MissingRequiredFieldSnafu { name: "Rows" })?
        .uint16()
        .context(CastValueSnafu { name: "Rows" })
}

/// Get the PhotoMetricInterpretation from the DICOM object
pub fn photometric_interpretation<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<String> {
    Ok(obj
        .element(dicom_dictionary_std::tags::PHOTOMETRIC_INTERPRETATION)
        .context(MissingRequiredFieldSnafu {
            name: "PhotometricInterpretation",
        })?
        .string()
        .context(CastValueSnafu {
            name: "PhotometricInterpretation",
        })?
        .trim()
        .to_string())
}

/// Get the SamplesPerPixel from the DICOM object
pub fn samples_per_pixel<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    obj.element(dicom_dictionary_std::tags::SAMPLES_PER_PIXEL)
        .context(MissingRequiredFieldSnafu {
            name: "SamplesPerPixel",
        })?
        .uint16()
        .context(CastValueSnafu {
            name: "SamplesPerPixel",
        })
}

/// Get the PlanarConfiguration from the DICOM object, returning 0 by default
#[cfg(not(feature = "gdcm"))]
pub fn planar_configuration<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> u16 {
    obj.element(dicom_dictionary_std::tags::PLANAR_CONFIGURATION)
        .map_or(Ok(0), |e| e.to_int())
        .unwrap_or(0)
}

/// Get the BitsAllocated from the DICOM object
pub fn bits_allocated<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    obj.element(dicom_dictionary_std::tags::BITS_ALLOCATED)
        .context(MissingRequiredFieldSnafu {
            name: "BitsAllocated",
        })?
        .uint16()
        .context(CastValueSnafu {
            name: "BitsAllocated",
        })
}

/// Get the BitsStored from the DICOM object
pub fn bits_stored<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    obj.element(dicom_dictionary_std::tags::BITS_STORED)
        .context(MissingRequiredFieldSnafu { name: "BitsStored" })?
        .uint16()
        .context(CastValueSnafu { name: "BitsStored" })
}

/// Get the HighBit from the DICOM object
pub fn high_bit<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    obj.element(dicom_dictionary_std::tags::HIGH_BIT)
        .context(MissingRequiredFieldSnafu { name: "HighBit" })?
        .uint16()
        .context(CastValueSnafu { name: "HighBit" })
}

/// Get the PixelRepresentation from the DICOM object
pub fn pixel_representation<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    obj.element(dicom_dictionary_std::tags::PIXEL_REPRESENTATION)
        .context(MissingRequiredFieldSnafu {
            name: "PixelRepresentation",
        })?
        .uint16()
        .context(CastValueSnafu {
            name: "PixelRepresentation",
        })
}

/// Get the PixelData element from the DICOM object
pub fn pixel_data<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<&InMemElement<D>> {
    obj.element(dicom_dictionary_std::tags::PIXEL_DATA)
        .context(MissingRequiredFieldSnafu { name: "PixelData" })
}

/// Get the RescaleIntercept from the DICOM object or returns 0
pub fn rescale_intercept<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> i16 {
    obj.element(dicom_dictionary_std::tags::RESCALE_INTERCEPT)
        .map_or(Ok(0), |e| e.to_int())
        .unwrap_or(0)
}

/// Get the RescaleSlope from the DICOM object or returns 1.0
pub fn rescale_slope<D: DataDictionary + Clone>(obj: &FileDicomObject<InMemDicomObject<D>>) -> f32 {
    obj.element(dicom_dictionary_std::tags::RESCALE_SLOPE)
        .map_or(Ok(1.0), |e| e.to_float32())
        .unwrap_or(1.0)
}

/// Get the NumberOfFrames from the DICOM object or returns 1
pub fn number_of_frames<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> u16 {
    obj.element(dicom_dictionary_std::tags::NUMBER_OF_FRAMES)
        .map_or(Ok(1), |e| e.to_int())
        .unwrap_or(1)
}
<commit_msg>[pixeldata] Refactor attribute module<commit_after>//! Utility module for fetching key attributes from a DICOM object.

use dicom_core::{DataDictionary, Tag};
use dicom_dictionary_std::tags;
use dicom_object::{mem::InMemElement, FileDicomObject, InMemDicomObject};
use snafu::{Backtrace, ResultExt, Snafu};

#[derive(Debug, Snafu)]
pub enum GetAttributeError {
    #[snafu(display("Missing required attribute `{}`", name))]
    MissingRequiredField {
        name: &'static str,
        #[snafu(backtrace)]
        source: dicom_object::Error,
    },

    #[snafu(display("Could not get attribute `{}`", name))]
    CastValue {
        name: &'static str,
        source: dicom_core::value::CastValueError,
        backtrace: Backtrace,
    },

    #[snafu(display("Could not convert attribute `{}`", name))]
    ConvertValue {
        name: &'static str,
        source: dicom_core::value::ConvertValueError,
        backtrace: Backtrace,
    },
}

pub type Result<T, E = GetAttributeError> = std::result::Result<T, E>;

/// Get the Columns from the DICOM object
pub fn cols<D: DataDictionary + Clone>(obj: &FileDicomObject<InMemDicomObject<D>>) -> Result<u16> {
    retrieve_required_u16(obj, tags::COLUMNS, "Columns")
}

/// Get the Rows from the DICOM object
pub fn rows<D: DataDictionary + Clone>(obj: &FileDicomObject<InMemDicomObject<D>>) -> Result<u16> {
    retrieve_required_u16(obj, tags::ROWS, "Rows")
}

/// Get the PhotoMetricInterpretation from the DICOM object
pub fn photometric_interpretation<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<String> {
    Ok(obj
        .element(tags::PHOTOMETRIC_INTERPRETATION)
        .context(MissingRequiredFieldSnafu {
            name: "PhotometricInterpretation",
        })?
        .string()
        .context(CastValueSnafu {
            name: "PhotometricInterpretation",
        })?
        .trim()
        .to_string())
}

/// Get the SamplesPerPixel from the DICOM object
pub fn samples_per_pixel<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    retrieve_required_u16(obj, tags::SAMPLES_PER_PIXEL, "SamplesPerPixel")
}

/// Get the PlanarConfiguration from the DICOM object, returning 0 by default
#[cfg(not(feature = "gdcm"))]
pub fn planar_configuration<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> u16 {
    obj.element(tags::PLANAR_CONFIGURATION)
        .map_or(Ok(0), |e| e.to_int())
        .unwrap_or(0)
}

/// Get the BitsAllocated from the DICOM object
pub fn bits_allocated<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    retrieve_required_u16(obj, tags::BITS_ALLOCATED, "BitsAllocated")
}

/// Get the BitsStored from the DICOM object
pub fn bits_stored<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    retrieve_required_u16(obj, tags::BITS_STORED, "BitsStored")
}

/// Get the HighBit from the DICOM object
pub fn high_bit<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    retrieve_required_u16(obj, tags::HIGH_BIT, "HighBit")
}

/// Get the PixelRepresentation from the DICOM object
pub fn pixel_representation<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<u16> {
    retrieve_required_u16(obj, tags::PIXEL_REPRESENTATION, "PixelRepresentation")
}

/// Get the PixelData element from the DICOM object
pub fn pixel_data<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<&InMemElement<D>> {
    obj.element(tags::PIXEL_DATA)
        .context(MissingRequiredFieldSnafu { name: "PixelData" })
}

/// Get the RescaleIntercept from the DICOM object or returns 0
pub fn rescale_intercept<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> i16 {
    obj.element(tags::RESCALE_INTERCEPT)
        .map_or(Ok(0), |e| e.to_int())
        .unwrap_or(0)
}

/// Get the RescaleSlope from the DICOM object or returns 1.0
pub fn rescale_slope<D: DataDictionary + Clone>(obj: &FileDicomObject<InMemDicomObject<D>>) -> f32 {
    obj.element(tags::RESCALE_SLOPE)
        .map_or(Ok(1.0), |e| e.to_float32())
        .unwrap_or(1.0)
}

/// Get the NumberOfFrames from the DICOM object or returns 1
pub fn number_of_frames<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> u16 {
    obj.element(tags::NUMBER_OF_FRAMES)
        .map_or(Ok(1), |e| e.to_int())
        .unwrap_or(1)
}

/// Retrieve the WindowCenter from the DICOM object if it exists.
pub fn window_center<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<Option<f64>> {
    retrieve_optional_to_f64(obj, tags::WINDOW_CENTER, "WindowCenter")
}

/// Retrieve the WindowWidth from the DICOM object if it exists.
pub fn window_width<D: DataDictionary + Clone>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
) -> Result<Option<f64>> {
    retrieve_optional_to_f64(obj, tags::WINDOW_WIDTH, "WindowWidth")
}

#[inline]
fn retrieve_required_u16<D>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
    tag: Tag,
    name: &'static str,
) -> Result<u16>
where
    D: DataDictionary + Clone,
{
    obj.element(tag)
        .context(MissingRequiredFieldSnafu { name })?
        .uint16()
        .context(CastValueSnafu { name })
}

#[inline]
fn retrieve_optional_to_f64<D>(
    obj: &FileDicomObject<InMemDicomObject<D>>,
    tag: Tag,
    name: &'static str,
) -> Result<Option<f64>>
where
    D: DataDictionary + Clone,
{
    let elem = match obj.element(tag) {
        Ok(e) => e,
        Err(dicom_object::Error::NoSuchDataElementTag { .. }) => return Ok(None),
        Err(e) => return Err(e).context(MissingRequiredFieldSnafu { name }),
    };

    elem.to_float64()
        .context(ConvertValueSnafu { name })
        .map(Some)
}
<|endoftext|>


<commit_before>//! Database migrations

use error::Result as CratesfyiResult;
use postgres::{Connection, transaction::Transaction, Error as PostgresError};
use schemamama::{Migration, Migrator, Version};
use schemamama_postgres::{PostgresAdapter, PostgresMigration};
use std::borrow::Cow;

#[derive(Copy, Clone)]
enum ApplyMode {
    Permanent,
    Temporary,
}

#[derive(Copy, Clone)]
struct MigrationContext {
    apply_mode: ApplyMode,
}

impl MigrationContext {
    fn format_query<'a>(&self, query: &'a str) -> Cow<'a, str> {
        match self.apply_mode {
            ApplyMode::Permanent => Cow::Borrowed(query),
            ApplyMode::Temporary => {
                Cow::Owned(query.replace("CREATE TABLE", "CREATE TEMPORARY TABLE"))
            }
        }
    }
}

/// Creates a new PostgresMigration from upgrade and downgrade queries.
/// Downgrade query should return database to previous state.
///
/// Example:
///
/// ```
/// let my_migration = migration!(100,
///                               "Create test table",
///                               "CREATE TABLE test ( id SERIAL);",
///                               "DROP TABLE test;");
/// ```
macro_rules! migration {
    ($context:expr, $version:expr, $description:expr, $up:expr, $down:expr $(,)?) => {{
        struct Amigration {
            ctx: MigrationContext,
        };
        impl Migration for Amigration {
            fn version(&self) -> Version {
                $version
            }
            fn description(&self) -> String {
                $description.to_owned()
            }
        }
        impl PostgresMigration for Amigration {
            fn up(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Applying migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($up)).map(|_| ())
            }
            fn down(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Removing migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($down)).map(|_| ())
            }
        }
        Box::new(Amigration { ctx: $context })
    }};
}

pub fn migrate(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Permanent)
}

pub fn migrate_temporary(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Temporary)
}

fn migrate_inner(version: Option<Version>, conn: &Connection, apply_mode: ApplyMode) -> CratesfyiResult<()> {
    let context = MigrationContext { apply_mode };

    conn.execute(
        &context.format_query(
            "CREATE TABLE IF NOT EXISTS database_versions (version BIGINT PRIMARY KEY);"
        ),
        &[],
    )?;
    let adapter = PostgresAdapter::with_metadata_table(conn, "database_versions");

    let mut migrator = Migrator::new(adapter);

    let migrations: Vec<Box<dyn PostgresMigration>> = vec![
        migration!(
            context,
            // version
            1,
            // description
            "Initial database schema",
            // upgrade query
            "CREATE TABLE crates (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255) UNIQUE NOT NULL,
                 latest_version_id INT DEFAULT 0,
                 versions JSON DEFAULT '[]',
                 downloads_total INT DEFAULT 0,
                 github_description VARCHAR(1024),
                 github_stars INT DEFAULT 0,
                 github_forks INT DEFAULT 0,
                 github_issues INT DEFAULT 0,
                 github_last_commit TIMESTAMP,
                 github_last_update TIMESTAMP,
                 content tsvector
             );
             CREATE TABLE releases (
                 id SERIAL PRIMARY KEY,
                 crate_id INT NOT NULL REFERENCES crates(id),
                 version VARCHAR(100),
                 release_time TIMESTAMP,
                 dependencies JSON,
                 target_name VARCHAR(255),
                 yanked BOOL DEFAULT FALSE,
                 is_library BOOL DEFAULT TRUE,
                 build_status BOOL DEFAULT FALSE,
                 rustdoc_status BOOL DEFAULT FALSE,
                 test_status BOOL DEFAULT FALSE,
                 license VARCHAR(100),
                 repository_url VARCHAR(255),
                 homepage_url VARCHAR(255),
                 documentation_url VARCHAR(255),
                 description VARCHAR(1024),
                 description_long VARCHAR(51200),
                 readme VARCHAR(51200),
                 authors JSON,
                 keywords JSON,
                 have_examples BOOL DEFAULT FALSE,
                 downloads INT DEFAULT 0,
                 files JSON,
                 doc_targets JSON DEFAULT '[]',
                 doc_rustc_version VARCHAR(100) NOT NULL,
                 default_target VARCHAR(100),
                 UNIQUE (crate_id, version)
             );
             CREATE TABLE authors (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 email VARCHAR(255),
                 slug VARCHAR(255) UNIQUE NOT NULL
             );
             CREATE TABLE author_rels (
                 rid INT REFERENCES releases(id),
                 aid INT REFERENCES authors(id),
                 UNIQUE(rid, aid)
             );
             CREATE TABLE keywords (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 slug VARCHAR(255) NOT NULL UNIQUE
             );
             CREATE TABLE keyword_rels (
                 rid INT REFERENCES releases(id),
                 kid INT REFERENCES keywords(id),
                 UNIQUE(rid, kid)
             );
             CREATE TABLE owners (
                 id SERIAL PRIMARY KEY,
                 login VARCHAR(255) NOT NULL UNIQUE,
                 avatar VARCHAR(255),
                 name VARCHAR(255),
                 email VARCHAR(255)
             );
             CREATE TABLE owner_rels (
                 cid INT REFERENCES releases(id),
                 oid INT REFERENCES owners(id),
                 UNIQUE(cid, oid)
             );
             CREATE TABLE builds (
                 id SERIAL,
                 rid INT NOT NULL REFERENCES releases(id),
                 rustc_version VARCHAR(100) NOT NULL,
                 cratesfyi_version VARCHAR(100) NOT NULL,
                 build_status BOOL NOT NULL,
                 build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 output TEXT
             );
             CREATE TABLE queue (
                 id SERIAL,
                 name VARCHAR(255),
                 version VARCHAR(100),
                 attempt INT DEFAULT 0,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 UNIQUE(name, version)
             );
             CREATE TABLE files (
                 path VARCHAR(4096) NOT NULL PRIMARY KEY,
                 mime VARCHAR(100) NOT NULL,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 content BYTEA
             );
             CREATE TABLE config (
                 name VARCHAR(100) NOT NULL PRIMARY KEY,
                 value JSON NOT NULL
             );
             CREATE INDEX ON releases (release_time DESC);
             CREATE INDEX content_idx ON crates USING gin(content);",
            // downgrade query
            "DROP TABLE authors, author_rels, keyword_rels, keywords, owner_rels,
                        owners, releases, crates, builds, queue, files, config;"
        ),
        migration!(
            context,
            // version
            2,
            // description
            "Added priority column to build queue",
            // upgrade query
            "ALTER TABLE queue ADD COLUMN priority INT DEFAULT 0;",
            // downgrade query
            "ALTER TABLE queue DROP COLUMN priority;"
        ),
        migration!(
            context,
            // version
            3,
            // description
            "Added sandbox_overrides table",
            // upgrade query
            "CREATE TABLE sandbox_overrides (
                 crate_name VARCHAR NOT NULL PRIMARY KEY,
                 max_memory_bytes INTEGER,
                 timeout_seconds INTEGER
             );",
            // downgrade query
            "DROP TABLE sandbox_overrides;"
        ),
        migration!(
            context,
            4,
            "Make more fields not null",
            "ALTER TABLE releases ALTER COLUMN release_time SET NOT NULL,
                                  ALTER COLUMN yanked SET NOT NULL,
                                  ALTER COLUMN downloads SET NOT NULL",
            "ALTER TABLE releases ALTER COLUMN release_time DROP NOT NULL,
                                  ALTER COLUMN yanked DROP NOT NULL,
                                  ALTER COLUMN downloads DROP NOT NULL"
        ),
        migration!(
            context,
            // version
            5,
            // description
            "Make target_name non-nullable",
            // upgrade query
            "ALTER TABLE releases ALTER COLUMN target_name SET NOT NULL",
            // downgrade query
            "ALTER TABLE releases ALTER COLUMN target_name DROP NOT NULL",
        ),
        migration!(
            context,
            // version
            6,
            // description
            "Added blacklisted_crates table",
            // upgrade query
            "CREATE TABLE blacklisted_crates (
                 crate_name VARCHAR NOT NULL PRIMARY KEY
             );",
            // downgrade query
            "DROP TABLE blacklisted_crates;"
        ),
        migration!(
            context,
            // version
            7,
            // description
            "Change sandbox_overrides's max_memory_bytes to BIGINT",
            // upgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE BIGINT;",
            // downgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE INTEGER;"
        ),
    ];

    for migration in migrations {
        migrator.register(migration);
    }

    if let Some(version) = version {
        if version > migrator.current_version()?.unwrap_or(0) {
            migrator.up(Some(version))?;
        } else {
            migrator.down(Some(version))?;
        }
    } else {
        migrator.up(version)?;
    }

    Ok(())
}
<commit_msg>Update migration description<commit_after>//! Database migrations

use error::Result as CratesfyiResult;
use postgres::{Connection, transaction::Transaction, Error as PostgresError};
use schemamama::{Migration, Migrator, Version};
use schemamama_postgres::{PostgresAdapter, PostgresMigration};
use std::borrow::Cow;

#[derive(Copy, Clone)]
enum ApplyMode {
    Permanent,
    Temporary,
}

#[derive(Copy, Clone)]
struct MigrationContext {
    apply_mode: ApplyMode,
}

impl MigrationContext {
    fn format_query<'a>(&self, query: &'a str) -> Cow<'a, str> {
        match self.apply_mode {
            ApplyMode::Permanent => Cow::Borrowed(query),
            ApplyMode::Temporary => {
                Cow::Owned(query.replace("CREATE TABLE", "CREATE TEMPORARY TABLE"))
            }
        }
    }
}

/// Creates a new PostgresMigration from upgrade and downgrade queries.
/// Downgrade query should return database to previous state.
///
/// Example:
///
/// ```
/// let my_migration = migration!(100,
///                               "Create test table",
///                               "CREATE TABLE test ( id SERIAL);",
///                               "DROP TABLE test;");
/// ```
macro_rules! migration {
    ($context:expr, $version:expr, $description:expr, $up:expr, $down:expr $(,)?) => {{
        struct Amigration {
            ctx: MigrationContext,
        };
        impl Migration for Amigration {
            fn version(&self) -> Version {
                $version
            }
            fn description(&self) -> String {
                $description.to_owned()
            }
        }
        impl PostgresMigration for Amigration {
            fn up(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Applying migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($up)).map(|_| ())
            }
            fn down(&self, transaction: &Transaction) -> Result<(), PostgresError> {
                info!("Removing migration {}: {}", self.version(), self.description());
                transaction.batch_execute(&self.ctx.format_query($down)).map(|_| ())
            }
        }
        Box::new(Amigration { ctx: $context })
    }};
}

pub fn migrate(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Permanent)
}

pub fn migrate_temporary(version: Option<Version>, conn: &Connection) -> CratesfyiResult<()> {
    migrate_inner(version, conn, ApplyMode::Temporary)
}

fn migrate_inner(version: Option<Version>, conn: &Connection, apply_mode: ApplyMode) -> CratesfyiResult<()> {
    let context = MigrationContext { apply_mode };

    conn.execute(
        &context.format_query(
            "CREATE TABLE IF NOT EXISTS database_versions (version BIGINT PRIMARY KEY);"
        ),
        &[],
    )?;
    let adapter = PostgresAdapter::with_metadata_table(conn, "database_versions");

    let mut migrator = Migrator::new(adapter);

    let migrations: Vec<Box<dyn PostgresMigration>> = vec![
        migration!(
            context,
            // version
            1,
            // description
            "Initial database schema",
            // upgrade query
            "CREATE TABLE crates (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255) UNIQUE NOT NULL,
                 latest_version_id INT DEFAULT 0,
                 versions JSON DEFAULT '[]',
                 downloads_total INT DEFAULT 0,
                 github_description VARCHAR(1024),
                 github_stars INT DEFAULT 0,
                 github_forks INT DEFAULT 0,
                 github_issues INT DEFAULT 0,
                 github_last_commit TIMESTAMP,
                 github_last_update TIMESTAMP,
                 content tsvector
             );
             CREATE TABLE releases (
                 id SERIAL PRIMARY KEY,
                 crate_id INT NOT NULL REFERENCES crates(id),
                 version VARCHAR(100),
                 release_time TIMESTAMP,
                 dependencies JSON,
                 target_name VARCHAR(255),
                 yanked BOOL DEFAULT FALSE,
                 is_library BOOL DEFAULT TRUE,
                 build_status BOOL DEFAULT FALSE,
                 rustdoc_status BOOL DEFAULT FALSE,
                 test_status BOOL DEFAULT FALSE,
                 license VARCHAR(100),
                 repository_url VARCHAR(255),
                 homepage_url VARCHAR(255),
                 documentation_url VARCHAR(255),
                 description VARCHAR(1024),
                 description_long VARCHAR(51200),
                 readme VARCHAR(51200),
                 authors JSON,
                 keywords JSON,
                 have_examples BOOL DEFAULT FALSE,
                 downloads INT DEFAULT 0,
                 files JSON,
                 doc_targets JSON DEFAULT '[]',
                 doc_rustc_version VARCHAR(100) NOT NULL,
                 default_target VARCHAR(100),
                 UNIQUE (crate_id, version)
             );
             CREATE TABLE authors (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 email VARCHAR(255),
                 slug VARCHAR(255) UNIQUE NOT NULL
             );
             CREATE TABLE author_rels (
                 rid INT REFERENCES releases(id),
                 aid INT REFERENCES authors(id),
                 UNIQUE(rid, aid)
             );
             CREATE TABLE keywords (
                 id SERIAL PRIMARY KEY,
                 name VARCHAR(255),
                 slug VARCHAR(255) NOT NULL UNIQUE
             );
             CREATE TABLE keyword_rels (
                 rid INT REFERENCES releases(id),
                 kid INT REFERENCES keywords(id),
                 UNIQUE(rid, kid)
             );
             CREATE TABLE owners (
                 id SERIAL PRIMARY KEY,
                 login VARCHAR(255) NOT NULL UNIQUE,
                 avatar VARCHAR(255),
                 name VARCHAR(255),
                 email VARCHAR(255)
             );
             CREATE TABLE owner_rels (
                 cid INT REFERENCES releases(id),
                 oid INT REFERENCES owners(id),
                 UNIQUE(cid, oid)
             );
             CREATE TABLE builds (
                 id SERIAL,
                 rid INT NOT NULL REFERENCES releases(id),
                 rustc_version VARCHAR(100) NOT NULL,
                 cratesfyi_version VARCHAR(100) NOT NULL,
                 build_status BOOL NOT NULL,
                 build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 output TEXT
             );
             CREATE TABLE queue (
                 id SERIAL,
                 name VARCHAR(255),
                 version VARCHAR(100),
                 attempt INT DEFAULT 0,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 UNIQUE(name, version)
             );
             CREATE TABLE files (
                 path VARCHAR(4096) NOT NULL PRIMARY KEY,
                 mime VARCHAR(100) NOT NULL,
                 date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                 content BYTEA
             );
             CREATE TABLE config (
                 name VARCHAR(100) NOT NULL PRIMARY KEY,
                 value JSON NOT NULL
             );
             CREATE INDEX ON releases (release_time DESC);
             CREATE INDEX content_idx ON crates USING gin(content);",
            // downgrade query
            "DROP TABLE authors, author_rels, keyword_rels, keywords, owner_rels,
                        owners, releases, crates, builds, queue, files, config;"
        ),
        migration!(
            context,
            // version
            2,
            // description
            "Added priority column to build queue",
            // upgrade query
            "ALTER TABLE queue ADD COLUMN priority INT DEFAULT 0;",
            // downgrade query
            "ALTER TABLE queue DROP COLUMN priority;"
        ),
        migration!(
            context,
            // version
            3,
            // description
            "Added sandbox_overrides table",
            // upgrade query
            "CREATE TABLE sandbox_overrides (
                 crate_name VARCHAR NOT NULL PRIMARY KEY,
                 max_memory_bytes INTEGER,
                 timeout_seconds INTEGER
             );",
            // downgrade query
            "DROP TABLE sandbox_overrides;"
        ),
        migration!(
            context,
            4,
            "Make more fields not null",
            "ALTER TABLE releases ALTER COLUMN release_time SET NOT NULL,
                                  ALTER COLUMN yanked SET NOT NULL,
                                  ALTER COLUMN downloads SET NOT NULL",
            "ALTER TABLE releases ALTER COLUMN release_time DROP NOT NULL,
                                  ALTER COLUMN yanked DROP NOT NULL,
                                  ALTER COLUMN downloads DROP NOT NULL"
        ),
        migration!(
            context,
            // version
            5,
            // description
            "Make target_name non-nullable",
            // upgrade query
            "ALTER TABLE releases ALTER COLUMN target_name SET NOT NULL",
            // downgrade query
            "ALTER TABLE releases ALTER COLUMN target_name DROP NOT NULL",
        ),
        migration!(
            context,
            // version
            6,
            // description
            "Added blacklisted_crates table",
            // upgrade query
            "CREATE TABLE blacklisted_crates (
                 crate_name VARCHAR NOT NULL PRIMARY KEY
             );",
            // downgrade query
            "DROP TABLE blacklisted_crates;"
        ),
        migration!(
            context,
            // version
            7,
            // description
            "Allow memory limits of more than 4 GB",
            // upgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE BIGINT;",
            // downgrade query
            "ALTER TABLE sandbox_overrides ALTER COLUMN max_memory_bytes TYPE INTEGER;"
        ),
    ];

    for migration in migrations {
        migrator.register(migration);
    }

    if let Some(version) = version {
        if version > migrator.current_version()?.unwrap_or(0) {
            migrator.up(Some(version))?;
        } else {
            migrator.down(Some(version))?;
        }
    } else {
        migrator.up(version)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::{File, create_dir};
use std::io::BufWriter;
use syn;
use syn::{DeriveInput, Body, VariantData, Ty, Path};
use super::representation::Representation;
use super::elm::{Module, Definition, Field, Type};

pub fn generate_elm(ast: &DeriveInput) {
    let mut path = env::current_dir().unwrap();
    path.push("generated");
    if path.exists() && path.is_file() { panic!("expecting \"generated\" to be a directory"); }
    if !path.exists() { create_dir(path.clone()).expect("problem creating \"generated\" directory") }
    let name = &ast.ident;
    path.push(format!("{}.elm", name));

    let file = File::create(path).unwrap();
    let ref mut w = BufWriter::new(file);

    let m = module_from(ast);

    m.write_representation(w).unwrap();
}

fn module_from(ast: &DeriveInput) -> Module {
    let name = &ast.ident;
    let mut module = Module::new(name.to_string());

    let definition = definition_from(&ast.body, name.to_string());
    module.define(definition);

    module
}

fn definition_from(body: &Body, name: String) -> Definition {
    match *body {
        Body::Enum(ref variants) => {
            let definition = Definition::Enum(name);
            definition
        },
        Body::Struct(ref variant_data) => {
            let mut definition = Definition::Record(name);
            match *variant_data {
                VariantData::Struct(ref fields) => {
                    for field in fields {
                        let field = field_from(field);
                        definition.add(field)
                    }
                },
                VariantData::Tuple(ref fields) => {
                    unimplemented!()
                },
                VariantData::Unit => {
                    unimplemented!()
                },
            }
            definition
        }
    }
}

fn field_from(field: &syn::Field) -> Field {
    let field_name = field.clone().ident.unwrap();
    let field_type = match field.ty {
        Ty::Path(_, ref path) => {
            let path_type = path.segments.last().unwrap();
            let elm_type = Type::from(path_type.ident.to_string().as_str());
            elm_type
        },
        _ => Type::Unknown,
    };
    let field = Field::new(field_name.to_string(), field_type);
    field
}
<commit_msg>Remove some warnings<commit_after>use std::env;
use std::fs::{File, create_dir};
use std::io::BufWriter;
use syn;
use syn::{DeriveInput, Body, VariantData, Ty};
use super::representation::Representation;
use super::elm::{Module, Definition, Field, Type};

pub fn generate_elm(ast: &DeriveInput) {
    let mut path = env::current_dir().unwrap();
    path.push("generated");
    if path.exists() && path.is_file() { panic!("expecting \"generated\" to be a directory"); }
    if !path.exists() { create_dir(path.clone()).expect("problem creating \"generated\" directory"); }
    let name = &ast.ident;
    path.push(format!("{}.elm", name));

    let file = File::create(path).unwrap();
    let ref mut w = BufWriter::new(file);

    let m = module_from(ast);

    m.write_representation(w).unwrap();
}

fn module_from(ast: &DeriveInput) -> Module {
    let name = &ast.ident;
    let mut module = Module::new(name.to_string());

    let definition = definition_from(&ast.body, name.to_string());
    module.define(definition);

    module
}

fn definition_from(body: &Body, name: String) -> Definition {
    match *body {
        Body::Enum(ref _variants) => {
            let definition = Definition::Enum(name);
            definition
        },
        Body::Struct(ref variant_data) => {
            let mut definition = Definition::Record(name);
            match *variant_data {
                VariantData::Struct(ref fields) => {
                    for field in fields {
                        let field = field_from(field);
                        definition.add(field)
                    }
                },
                VariantData::Tuple(ref _fields) => {
                    unimplemented!()
                },
                VariantData::Unit => {
                    unimplemented!()
                },
            }
            definition
        }
    }
}

fn field_from(field: &syn::Field) -> Field {
    let field_name = field.clone().ident.unwrap();
    let field_type = match field.ty {
        Ty::Path(_, ref path) => {
            let path_type = path.segments.last().unwrap();
            let elm_type = Type::from(path_type.ident.to_string().as_str());
            elm_type
        },
        _ => Type::Unknown,
    };
    let field = Field::new(field_name.to_string(), field_type);
    field
}
<|endoftext|>


<commit_before>use std::fmt;

use bytes::Bytes;

/// A piece of a message body.
pub struct Chunk(Inner);

enum Inner {
    Shared(Bytes),
}

impl From<Vec<u8>> for Chunk {
    #[inline]
    fn from(v: Vec<u8>) -> Chunk {
        Chunk::from(Bytes::from(v))
    }
}

impl From<&'static [u8]> for Chunk {
    #[inline]
    fn from(slice: &'static [u8]) -> Chunk {
        Chunk::from(Bytes::from_static(slice))
    }
}

impl From<String> for Chunk {
    #[inline]
    fn from(s: String) -> Chunk {
        s.into_bytes().into()
    }
}

impl From<&'static str> for Chunk {
    #[inline]
    fn from(slice: &'static str) -> Chunk {
        slice.as_bytes().into()
    }
}

impl From<Bytes> for Chunk {
    fn from(mem: Bytes) -> Chunk {
        Chunk(Inner::Shared(mem))
    }
}

impl From<Chunk> for Bytes {
    fn from(chunk: Chunk) -> Bytes {
        match self.0 {
            Inner::Shared(bytes) => bytes,
        }
    }
}

impl ::std::ops::Deref for Chunk {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

impl AsRef<[u8]> for Chunk {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        match self.0 {
            Inner::Shared(ref slice) => slice,
        }
    }
}

impl fmt::Debug for Chunk {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.as_ref(), f)
    }
}
<commit_msg>fix(http): Fix commit fac3d70c0b716157ba689ae2b8a0089b6afc9bdc<commit_after>use std::fmt;

use bytes::Bytes;

/// A piece of a message body.
pub struct Chunk(Inner);

enum Inner {
    Shared(Bytes),
}

impl From<Vec<u8>> for Chunk {
    #[inline]
    fn from(v: Vec<u8>) -> Chunk {
        Chunk::from(Bytes::from(v))
    }
}

impl From<&'static [u8]> for Chunk {
    #[inline]
    fn from(slice: &'static [u8]) -> Chunk {
        Chunk::from(Bytes::from_static(slice))
    }
}

impl From<String> for Chunk {
    #[inline]
    fn from(s: String) -> Chunk {
        s.into_bytes().into()
    }
}

impl From<&'static str> for Chunk {
    #[inline]
    fn from(slice: &'static str) -> Chunk {
        slice.as_bytes().into()
    }
}

impl From<Bytes> for Chunk {
    fn from(mem: Bytes) -> Chunk {
        Chunk(Inner::Shared(mem))
    }
}

impl From<Chunk> for Bytes {
    fn from(chunk: Chunk) -> Bytes {
        match chunk.0 {
            Inner::Shared(bytes) => bytes,
        }
    }
}

impl ::std::ops::Deref for Chunk {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.as_ref()
    }
}

impl AsRef<[u8]> for Chunk {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        match self.0 {
            Inner::Shared(ref slice) => slice,
        }
    }
}

impl fmt::Debug for Chunk {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(self.as_ref(), f)
    }
}
<|endoftext|>


<commit_before>use bindings::*;
use context::Context;
use function::Function;
use util::NativeRef;
use std::fmt::Show;
use std::str::raw::from_c_str;
use std::c_str::ToCStr;
/// An ELF binary reader
native_ref!(ReadElf, _reader, jit_readelf_t)
impl ReadElf {
	/// Open a new ELF binary
	pub fn new<S:ToCStr+Show>(filename:S) -> ReadElf {
		unsafe {
			let mut this = RawPtr::null();
			let code = filename.with_c_str(|c_name|
				jit_readelf_open(&mut this, c_name, 0)
			);
			if this.is_null() {
				fail!("'{}' couldn't be opened due to {}", filename, code);
			} else {
				NativeRef::from_ptr(this)
			}
		}
	}
	#[inline]
	/// Get the name of this ELF binary
	pub fn get_name(&self) -> String {
		unsafe {
			from_c_str(jit_readelf_get_name(self.as_ptr()))
		}
	}
	#[inline]
	pub fn add_to_context(&self, ctx:&Context) {
		unsafe {
			jit_readelf_add_to_context(self.as_ptr(), ctx.as_ptr())
		}
	}
	#[inline]
	/// Get a simple in the ELF binary
	pub unsafe fn get_symbol<T, S:ToCStr>(&self, symbol:S) -> *T {
		symbol.with_c_str(|c_symbol|
			jit_readelf_get_symbol(self.as_ptr(), c_symbol) as *T
		)
	}
}
impl Drop for ReadElf {
	#[inline]
	fn drop(&mut self) {
		unsafe {
			jit_readelf_close(self.as_ptr())
		}
	}
}

/// An ELF binary reader
native_ref!(WriteElf, _writer, jit_writeelf_t)
impl WriteElf {
	#[inline]
	/// Create a new ELF binary reader
	pub fn new<S:ToCStr>(lib_name:S) -> WriteElf {
		lib_name.with_c_str(|c_lib_name| unsafe {
			NativeRef::from_ptr(jit_writeelf_create(c_lib_name))
		})
	}
	#[inline]
	/// Write to the filename given (not implemented by LibJIT yet)
	pub fn write<S:ToCStr>(&self, filename:S) -> bool {
		filename.with_c_str(|c_filename| unsafe {
			jit_writeelf_write(self.as_ptr(), c_filename) != 0
		})
	}
	#[inline]
	/// Add a function to the ELF
	pub fn add_function<S:ToCStr>(&self, func:&Function, name:S) -> bool {
		name.with_c_str(|c_name| unsafe {
			jit_writeelf_add_function(self.as_ptr(), func.as_ptr(), c_name) != 0
		})
	}
	#[inline]
	/// Add a dependency to the ELF
	pub fn add_needed<S:ToCStr>(&self, lib_name:S) -> bool {
		lib_name.with_c_str(|c_lib_name| unsafe {
			jit_writeelf_add_needed(self.as_ptr(), c_lib_name) != 0
		})
	}
}
impl Drop for WriteElf {
	#[inline]
	fn drop(&mut self) {
		unsafe {
			jit_writeelf_destroy(self.as_ptr())
		}
	}
}
#[test]
fn test_elf() {
	let elf = ReadElf::new("/usr/lib/libjit.so.0");
	assert_eq!(elf.get_name().as_slice(), "libjit.so.0");
}<commit_msg>Correct typo in ELF reader docs<commit_after>use bindings::*;
use context::Context;
use function::Function;
use util::NativeRef;
use std::fmt::Show;
use std::str::raw::from_c_str;
use std::c_str::ToCStr;
/// An ELF binary reader
native_ref!(ReadElf, _reader, jit_readelf_t)
impl ReadElf {
	/// Open a new ELF binary
	pub fn new<S:ToCStr+Show>(filename:S) -> ReadElf {
		unsafe {
			let mut this = RawPtr::null();
			let code = filename.with_c_str(|c_name|
				jit_readelf_open(&mut this, c_name, 0)
			);
			if this.is_null() {
				fail!("'{}' couldn't be opened due to {}", filename, code);
			} else {
				NativeRef::from_ptr(this)
			}
		}
	}
	#[inline]
	/// Get the name of this ELF binary
	pub fn get_name(&self) -> String {
		unsafe {
			from_c_str(jit_readelf_get_name(self.as_ptr()))
		}
	}
	#[inline]
	pub fn add_to_context(&self, ctx:&Context) {
		unsafe {
			jit_readelf_add_to_context(self.as_ptr(), ctx.as_ptr())
		}
	}
	#[inline]
	/// Get a symbol in the ELF binary
	pub unsafe fn get_symbol<T, S:ToCStr>(&self, symbol:S) -> *T {
		symbol.with_c_str(|c_symbol|
			jit_readelf_get_symbol(self.as_ptr(), c_symbol) as *T
		)
	}
}
impl Drop for ReadElf {
	#[inline]
	fn drop(&mut self) {
		unsafe {
			jit_readelf_close(self.as_ptr())
		}
	}
}

/// An ELF binary reader
native_ref!(WriteElf, _writer, jit_writeelf_t)
impl WriteElf {
	#[inline]
	/// Create a new ELF binary reader
	pub fn new<S:ToCStr>(lib_name:S) -> WriteElf {
		lib_name.with_c_str(|c_lib_name| unsafe {
			NativeRef::from_ptr(jit_writeelf_create(c_lib_name))
		})
	}
	#[inline]
	/// Write to the filename given (not implemented by LibJIT yet)
	pub fn write<S:ToCStr>(&self, filename:S) -> bool {
		filename.with_c_str(|c_filename| unsafe {
			jit_writeelf_write(self.as_ptr(), c_filename) != 0
		})
	}
	#[inline]
	/// Add a function to the ELF
	pub fn add_function<S:ToCStr>(&self, func:&Function, name:S) -> bool {
		name.with_c_str(|c_name| unsafe {
			jit_writeelf_add_function(self.as_ptr(), func.as_ptr(), c_name) != 0
		})
	}
	#[inline]
	/// Add a dependency to the ELF
	pub fn add_needed<S:ToCStr>(&self, lib_name:S) -> bool {
		lib_name.with_c_str(|c_lib_name| unsafe {
			jit_writeelf_add_needed(self.as_ptr(), c_lib_name) != 0
		})
	}
}
impl Drop for WriteElf {
	#[inline]
	fn drop(&mut self) {
		unsafe {
			jit_writeelf_destroy(self.as_ptr())
		}
	}
}
#[test]
fn test_elf() {
	let elf = ReadElf::new("/usr/lib/libjit.so.0");
	assert_eq!(elf.get_name().as_slice(), "libjit.so.0");
}<|endoftext|>


<commit_before>use std::io::{File, IoError};
use std::io::fs::PathExtensions;
use libc::funcs::c95::stdlib::system;
#[cfg(test)] use test_helper::{cleanup_file, random_name};

/// Copy and create the new project file from a template.
pub fn create(path: &Path, content: &str) -> Result<(), IoError>{
    File::create(path).write(content.as_bytes())
}

/// Open a project file with the default editor. Uses C directly to interact
/// with the system. This method is overloaded below for the test config to not
/// execture during testing.
#[cfg(not(test))] pub fn open(path: &Path) {
    let method = format!("$EDITOR {}", path.display()).to_c_str();
    unsafe { system(method.unwrap()); };
}

/// Overloaded method for use in testing. Doesn't do anything at all.
#[cfg(test)] pub fn open(_path: &Path) { }

#[test]
fn creates_file() {
    let path = &Path::new(format!("/tmp/project_file_{}.toml", random_name()));
    create(path, "");
    assert!(path.exists());

    cleanup_file(path);
}

#[test]
fn creates_file_with_content() {
    let path    = &Path::new(format!("/tmp/project_file_{}.toml", random_name()));
    let content = "this content";
    create(path, content);

    let file_contents = File::open(path).read_to_end().unwrap();
    let read_contents = String::from_utf8(file_contents);

    assert_eq!(content, read_contents.unwrap().as_slice());

    cleanup_file(path);
}

#[test]
fn creates_files_without_error () {
    let path   = &Path::new(format!("/tmp/project_file_{}.toml", random_name()));
    let result = create(path, "");

    assert!(result.is_ok());
    cleanup_file(path);
}

#[test]
fn errors_when_creating_project_file() {
    let path = &Path::new("");
    let result = create(path, "");
    assert!(result.is_err());
}
<commit_msg>Remove compiler warnings.<commit_after>use std::io::{File, IoError};
use std::io::fs::PathExtensions;
#[cfg(not(test))] use libc::funcs::c95::stdlib::system;
#[cfg(test)] use test_helper::{cleanup_file, random_name};

/// Copy and create the new project file from a template.
pub fn create(path: &Path, content: &str) -> Result<(), IoError>{
    File::create(path).write(content.as_bytes())
}

/// Open a project file with the default editor. Uses C directly to interact
/// with the system. This method is overloaded below for the test config to not
/// execture during testing.
#[cfg(not(test))] pub fn open(path: &Path) {
    let method = format!("$EDITOR {}", path.display()).to_c_str();
    unsafe { system(method.unwrap()); };
}

/// Overloaded method for use in testing. Doesn't do anything at all.

#[allow(dead_code)]
#[cfg(test)] pub fn open(_path: &Path) { }

#[test]
fn creates_file() {
    let path = &Path::new(format!("/tmp/project_file_{}.toml", random_name()));
    let _result = create(path, "");
    assert!(path.exists());

    cleanup_file(path);
}

#[test]
fn creates_file_with_content() {
    let path    = &Path::new(format!("/tmp/project_file_{}.toml", random_name()));
    let content = "this content";
    let _result = create(path, content);

    let file_contents = File::open(path).read_to_end().unwrap();
    let read_contents = String::from_utf8(file_contents);

    assert_eq!(content, read_contents.unwrap().as_slice());

    cleanup_file(path);
}

#[test]
fn creates_files_without_error () {
    let path   = &Path::new(format!("/tmp/project_file_{}.toml", random_name()));
    let result = create(path, "");

    assert!(result.is_ok());
    cleanup_file(path);
}

#[test]
fn errors_when_creating_project_file() {
    let path = &Path::new("");
    let result = create(path, "");
    assert!(result.is_err());
}
<|endoftext|>


<commit_before>use timing_analyzer;
use error_analyzer;

#[derive(PartialEq, Debug)]
pub struct RequestLogAnalyzerResult {
    pub count: usize,
    pub timing: Option<timing_analyzer::TimingResult>,
    pub error: Option<error_analyzer::ErrorRatesResult>,
}

#[cfg(test)]
mod tests {
    use super::*;
}
<commit_msg>Boilerplate cleanup<commit_after>use timing_analyzer;
use error_analyzer;

#[derive(PartialEq, Debug)]
pub struct RequestLogAnalyzerResult {
    pub count: usize,
    pub timing: Option<timing_analyzer::TimingResult>,
    pub error: Option<error_analyzer::ErrorRatesResult>,
}
<|endoftext|>


<commit_before>use regex::Regex;
use http::method::{Method, Options};
use http::status::{InternalServerError};
use iron::{Ingot, Request, Response, Alloy};
use iron::ingot::{Status, Continue, Unwind};

pub mod params;
mod glob;

pub type Handler<Rq, Rs> = fn(&mut Rq, &mut Rs, &mut Alloy) -> ();

#[deriving(Clone)]
pub struct Router<Rq, Rs> {
    options: Vec<Method>,
    routes: Vec<Route<Rq, Rs>>
}

struct Route<Rq, Rs> {
    method: Method,
    glob: String,
    matches: Regex,
    handler: Handler<Rq, Rs>,
    params: Vec<String>
}

impl<Rq, Rs> Clone for Route<Rq, Rs> {
    fn clone(&self) -> Route<Rq, Rs> {
        Route {
            method: self.method.clone(),
            glob: self.glob.clone(),
            matches: self.matches.clone(),
            handler: self.handler,
            params: self.params.clone()
        }
    }
}

impl<Rq, Rs> Router<Rq, Rs> {
    pub fn new() -> Router<Rq, Rs> { Router { options: Vec::new(), routes: Vec::new() } }
    pub fn route(&mut self, method: Method, glob: String,
                 params: Vec<String>, handler: Handler<Rq, Rs>) {
        self.add_route(Route {
            method: method,
            matches: glob::deglob(glob.clone()),
            params: params,
            handler: handler,
            glob: glob
        });
    }

    fn add_route(&mut self, route: Route<Rq, Rs>) {
        if !self.options.contains(&route.method) {
            self.options.push(route.method.clone())
        }
        self.routes.push(route);
    }
}

impl<Rq: Request + Clone, Rs: Response + Clone> Ingot<Rq, Rs> for Router<Rq, Rs> {
    fn enter(&mut self, req: &mut Rq, res: &mut Rs, alloy: &mut Alloy) -> Status {
        if *req.method() == Options {
            match res.write(
                self.options.iter()
                    .map(|p| format!("{}", p))
                    .collect::<Vec<String>>()
                    .connect(" ").as_bytes()) {
                Ok(_) => {},
                Err(err) => {
                    error!("Failed to write response: {}", err);
                    *res.status_mut() = InternalServerError;
                }
            }
            return Unwind;
        }
        for route in self.routes.iter() {
            if route.matches.is_match(format!("{}", req.uri()).as_slice()) {
                alloy.insert::<params::Params>(
                    params::Params::new(
                        format!("{}", req.uri()).as_slice(),
                        route.matches.clone(),
                        route.params.clone().move_iter()
                    )
                );
                (route.handler)(req, res, alloy);
                return Unwind;
            }
        }
        Continue
    }
}

<commit_msg>(fix) Eliminated Clone requirement for Rq and Rs.<commit_after>use regex::Regex;
use http::method::{Method, Options};
use http::status::{InternalServerError};
use iron::{Ingot, Request, Response, Alloy};
use iron::ingot::{Status, Continue, Unwind};

pub mod params;
mod glob;

pub type Handler<Rq, Rs> = fn(&mut Rq, &mut Rs, &mut Alloy) -> ();

pub struct Router<Rq, Rs> {
    options: Vec<Method>,
    routes: Vec<Route<Rq, Rs>>
}

impl<Rq, Rs> Clone for Router<Rq, Rs> {
    fn clone(&self) -> Router<Rq, Rs> {
        Router {
            options: self.options.clone(),
            routes: self.routes.clone()
        }
    }
}

struct Route<Rq, Rs> {
    method: Method,
    glob: String,
    matches: Regex,
    handler: Handler<Rq, Rs>,
    params: Vec<String>
}

impl<Rq, Rs> Clone for Route<Rq, Rs> {
    fn clone(&self) -> Route<Rq, Rs> {
        Route {
            method: self.method.clone(),
            glob: self.glob.clone(),
            matches: self.matches.clone(),
            handler: self.handler,
            params: self.params.clone()
        }
    }
}

impl<Rq, Rs> Router<Rq, Rs> {
    pub fn new() -> Router<Rq, Rs> { Router { options: Vec::new(), routes: Vec::new() } }
    pub fn route(&mut self, method: Method, glob: String,
                 params: Vec<String>, handler: Handler<Rq, Rs>) {
        self.add_route(Route {
            method: method,
            matches: glob::deglob(glob.clone()),
            params: params,
            handler: handler,
            glob: glob
        });
    }

    fn add_route(&mut self, route: Route<Rq, Rs>) {
        if !self.options.contains(&route.method) {
            self.options.push(route.method.clone())
        }
        self.routes.push(route);
    }
}

impl<Rq: Request, Rs: Response> Ingot<Rq, Rs> for Router<Rq, Rs> {
    fn enter(&mut self, req: &mut Rq, res: &mut Rs, alloy: &mut Alloy) -> Status {
        if *req.method() == Options {
            match res.write(
                self.options.iter()
                    .map(|p| format!("{}", p))
                    .collect::<Vec<String>>()
                    .connect(" ").as_bytes()) {
                Ok(_) => {},
                Err(err) => {
                    error!("Failed to write response: {}", err);
                    *res.status_mut() = InternalServerError;
                }
            }
            return Unwind;
        }
        for route in self.routes.iter() {
            if route.matches.is_match(format!("{}", req.uri()).as_slice()) {
                alloy.insert::<params::Params>(
                    params::Params::new(
                        format!("{}", req.uri()).as_slice(),
                        route.matches.clone(),
                        route.params.clone().move_iter()
                    )
                );
                (route.handler)(req, res, alloy);
                return Unwind;
            }
        }
        Continue
    }
}

<|endoftext|>


<commit_before>//! Implements the searching of the game tree.

pub mod alpha_beta;
pub mod threading;
pub mod deepening;

use std::thread;
use std::sync::Arc;
use std::time::{SystemTime, Duration};
use basetypes::*;
use moves::*;
use tt::*;
use position::Position;
use self::threading::*;
use self::deepening::{SearchExecutor, DeepeningSearcher};

/// The maximum search depth in half-moves.
pub const MAX_DEPTH: u8 = 63; // Should be less than 127.

/// A sequence of moves from some starting position, together with the
/// value assigned to the final position.
pub struct Variation {
    /// A sequence of moves from some starting position.
    pub moves: Vec<Move>,

    /// The value assigned to the final position.
    pub value: Value,

    /// The accuracy of the assigned value.
    pub bound: BoundType,
}

/// Contains information about the current progress of a search.
pub struct SearchStatus {
    /// The starting time for the search.
    pub started_at: SystemTime,

    /// The starting (root) position for the search.
    pub position: Position,

    /// `true` if the search has stopped, `false` otherwise.
    pub done: bool,

    /// The reached search depth.
    pub depth: u8,

    /// The number of legal moves in the starting (root) position.
    pub legal_moves_count: usize,

    /// The best variations found so far. The first move in each
    /// variation will be different.
    pub variations: Vec<Variation>,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// The number of analyzed nodes.
    pub searched_nodes: NodeCount,

    /// Average number of analyzed nodes per second.
    pub nps: NodeCount,
}

/// A thread that executes consecutive searches in different starting
/// positions.
pub struct SearchThread {
    tt: Arc<Tt>,
    status: SearchStatus,
    searcher: DeepeningSearcher,
}

impl SearchThread {
    /// Creates a new instance.
    pub fn new(tt: Arc<Tt>) -> SearchThread {
        SearchThread {
            tt: tt.clone(),
            status: SearchStatus {
                started_at: SystemTime::now(),
                position: Position::from_fen(::STARTING_POSITION).ok().unwrap(),
                done: true,
                depth: 0,
                variations: vec![Variation {
                                     value: VALUE_MIN,
                                     bound: BOUND_LOWER,
                                     moves: vec![],
                                 }],
                legal_moves_count: 20,
                searched_nodes: 0,
                duration_millis: 0,
                nps: 0,
            },
            searcher: DeepeningSearcher::new(tt),
        }
    }

    /// Stops the current search, starts a new search, updates the
    /// status.
    ///
    /// `position` is the starting position for the new
    /// search. `variation_count` specifies how many best lines of
    /// play to calculate (the first move in each line will be
    /// different). `searchmoves` restricts the analysis to the
    /// supplied list of moves only (no restrictions if the suppied
    /// list is empty). The move format is long algebraic
    /// notation. Examples: e2e4, e7e5, e1g1 (white short castling),
    /// e7e8q (for promotion).
    #[allow(unused_variables)]
    pub fn search(&mut self,
                  position: &Position,
                  variation_count: usize,
                  mut searchmoves: Vec<String>) {
        // Find all legal moves.
        let mut legal_moves = vec![];
        let mut move_stack = MoveStack::new();
        let mut p = position.clone();
        p.generate_moves(&mut move_stack);
        while let Some(m) = move_stack.pop() {
            if p.do_move(m) {
                legal_moves.push(m);
                p.undo_move();
            }
        }

        let legal_moves_count = legal_moves.len();

        // Remove all legal moves not allowed by `searchmoves`.
        if !searchmoves.is_empty() {
            searchmoves.sort();
            let mut moves = vec![];
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
            if !moves.is_empty() {
                legal_moves = moves;
            }
        };

        self.stop();
        self.searcher.start_search(0,
                                   position.clone(),
                                   MAX_DEPTH,
                                   VALUE_MIN,
                                   VALUE_MAX,
                                   VALUE_UNKNOWN,
                                   legal_moves,
                                   variation_count);

        self.status = SearchStatus {
            started_at: SystemTime::now(),
            position: p,
            done: false,
            depth: 0,
            legal_moves_count: legal_moves_count,
            variations: vec![Variation {
                                 value: VALUE_MIN,
                                 bound: BOUND_LOWER,
                                 moves: vec![],
                             }], // TODO: Set good initial value (vec![best_move]).
            searched_nodes: 0,
            duration_millis: 0,
            ..self.status
        };
    }

    /// Stops the current search, updates the status.
    ///
    /// Does nothing if the current search is already stopped.
    pub fn stop(&mut self) {
        self.searcher.terminate_search();
        while !self.status().done {
            if let Ok(r) = self.searcher.try_recv_report() {
                self.process_report(r);
            } else {
                thread::sleep(Duration::from_millis(1));
            }
        }
    }

    /// Returns the status of the current search.
    ///
    /// **Important note:** Consecutive calls to this method will
    /// return the same unchanged result. Only after calling `search`,
    /// `stop`, or `wait_status_update`, the result returned by
    /// `status` may change.
    #[inline(always)]
    pub fn status(&self) -> &SearchStatus {
        &self.status
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    pub fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// Stops the current search and joins the search thread.
    ///
    /// After calling `join`, no other methods on this instance should
    /// be called.
    pub fn join(&mut self) {
        self.stop();
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: Report) {
        let duration = self.status.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        if self.status.depth < report.depth {
            self.status.depth = report.depth;
            self.status.variations = vec![extract_pv(&self.tt,
                                                     &self.status.position,
                                                     report.depth)];
        }
        self.status.done = report.done;
    }
}

/// A helper function. It extracts the primary variation (PV) from the
/// transposition table (TT) and returns it.
///
/// **Note:** Because the PV is a moving target (the search continues
/// to run in parallel), imperfections in the reported PVs are
/// unavoidable. To deal with this, we turn a blind eye if the value
/// at the root of the PV differs from the value at the leaf by no
/// more than `EPSILON`.
fn extract_pv(tt: &Tt, position: &Position, depth: u8) -> Variation {
    const EPSILON: Value = 8; // A sufficiently small value (in centipawns).

    // Extract the PV, the leaf value, the root value, and the bound
    // type from the TT.
    let mut p = position.clone();
    let mut our_turn = true;
    let mut prev_move = None;
    let mut moves = Vec::new();
    let mut leaf_value = -9999;
    let mut root_value = leaf_value;
    let mut bound = BOUND_LOWER;
    while let Some(entry) = tt.peek(p.hash()) {
        if entry.bound() != BOUND_NONE {
            // Get the value and the bound type. In half of the
            // cases the value stored in `entry` is from other
            // side's perspective.
            if our_turn {
                leaf_value = entry.value();
                bound = entry.bound();
            } else {
                leaf_value = -entry.value();
                bound = match entry.bound() {
                    BOUND_UPPER => BOUND_LOWER,
                    BOUND_LOWER => BOUND_UPPER,
                    x => x,
                };
            }

            // The values under -9999 and over 9999 may look ugly in
            // some GUIs, so we trim them.
            if leaf_value > 9999 {
                leaf_value = 9999;
                if bound == BOUND_LOWER {
                    bound = BOUND_EXACT
                }
            }
            if leaf_value < -9999 {
                leaf_value = 9999;
                if bound == BOUND_UPPER {
                    bound = BOUND_EXACT
                }
            }

            if let Some(m) = prev_move {
                // Extend the PV with the previous move.
                moves.push(m);
            } else {
                // We are at the root -- set the root value.
                root_value = leaf_value;
            }

            if moves.len() < depth as usize && (leaf_value - root_value).abs() <= EPSILON {
                if let Some(m) = p.try_move_digest(entry.move16()) {
                    if p.do_move(m) {
                        if bound == BOUND_EXACT {
                            // Extend the PV with one more move.
                            prev_move = Some(m);
                            our_turn = !our_turn;
                            continue;
                        } else {
                            // This is the last move in the PV.
                            moves.push(m);
                        }
                    }
                }
            }
        }
        break;
    }

    // Change the bound type if the leaf value in the PV differs too
    // much from the root value.
    bound = match leaf_value - root_value {
        x if x > EPSILON && bound != BOUND_UPPER => BOUND_LOWER,
        x if x < -EPSILON && bound != BOUND_LOWER => BOUND_UPPER,
        _ => bound,
    };

    Variation {
        value: root_value,
        bound: bound,
        moves: moves,
    }
}
<commit_msg>minor improvement<commit_after>//! Implements the searching of the game tree.

pub mod alpha_beta;
pub mod threading;
pub mod deepening;

use std::sync::Arc;
use std::time::{SystemTime, Duration};
use basetypes::*;
use moves::*;
use tt::*;
use position::Position;
use self::threading::*;
use self::deepening::{SearchExecutor, DeepeningSearcher};

/// The maximum search depth in half-moves.
pub const MAX_DEPTH: u8 = 63; // Should be less than 127.

/// A sequence of moves from some starting position, together with the
/// value assigned to the final position.
pub struct Variation {
    /// A sequence of moves from some starting position.
    pub moves: Vec<Move>,

    /// The value assigned to the final position.
    pub value: Value,

    /// The accuracy of the assigned value.
    pub bound: BoundType,
}

/// Contains information about the current progress of a search.
pub struct SearchStatus {
    /// The starting time for the search.
    pub started_at: SystemTime,

    /// The starting (root) position for the search.
    pub position: Position,

    /// `true` if the search has stopped, `false` otherwise.
    pub done: bool,

    /// The reached search depth.
    pub depth: u8,

    /// The number of legal moves in the starting (root) position.
    pub legal_moves_count: usize,

    /// The best variations found so far. The first move in each
    /// variation will be different.
    pub variations: Vec<Variation>,

    /// The duration of the search in milliseconds.
    pub duration_millis: u64,

    /// The number of analyzed nodes.
    pub searched_nodes: NodeCount,

    /// Average number of analyzed nodes per second.
    pub nps: NodeCount,
}

/// A thread that executes consecutive searches in different starting
/// positions.
pub struct SearchThread {
    tt: Arc<Tt>,
    status: SearchStatus,
    searcher: DeepeningSearcher,
}

impl SearchThread {
    /// Creates a new instance.
    pub fn new(tt: Arc<Tt>) -> SearchThread {
        SearchThread {
            tt: tt.clone(),
            status: SearchStatus {
                started_at: SystemTime::now(),
                position: Position::from_fen(::STARTING_POSITION).ok().unwrap(),
                done: true,
                depth: 0,
                variations: vec![Variation {
                                     value: VALUE_MIN,
                                     bound: BOUND_LOWER,
                                     moves: vec![],
                                 }],
                legal_moves_count: 20,
                searched_nodes: 0,
                duration_millis: 0,
                nps: 0,
            },
            searcher: DeepeningSearcher::new(tt),
        }
    }

    /// Stops the current search, starts a new search, updates the
    /// status.
    ///
    /// `position` is the starting position for the new
    /// search. `variation_count` specifies how many best lines of
    /// play to calculate (the first move in each line will be
    /// different). `searchmoves` restricts the analysis to the
    /// supplied list of moves only (no restrictions if the suppied
    /// list is empty). The move format is long algebraic
    /// notation. Examples: e2e4, e7e5, e1g1 (white short castling),
    /// e7e8q (for promotion).
    #[allow(unused_variables)]
    pub fn search(&mut self,
                  position: &Position,
                  variation_count: usize,
                  mut searchmoves: Vec<String>) {
        // Find all legal moves.
        let mut legal_moves = vec![];
        let mut move_stack = MoveStack::new();
        let mut p = position.clone();
        p.generate_moves(&mut move_stack);
        while let Some(m) = move_stack.pop() {
            if p.do_move(m) {
                legal_moves.push(m);
                p.undo_move();
            }
        }

        let legal_moves_count = legal_moves.len();

        // Remove all legal moves not allowed by `searchmoves`.
        if !searchmoves.is_empty() {
            searchmoves.sort();
            let mut moves = vec![];
            for m in legal_moves.iter() {
                if searchmoves.binary_search(&m.notation()).is_ok() {
                    moves.push(*m);
                }
            }
            if !moves.is_empty() {
                legal_moves = moves;
            }
        };

        self.stop();
        self.searcher.start_search(0,
                                   position.clone(),
                                   MAX_DEPTH,
                                   VALUE_MIN,
                                   VALUE_MAX,
                                   VALUE_UNKNOWN,
                                   legal_moves,
                                   variation_count);

        self.status = SearchStatus {
            started_at: SystemTime::now(),
            position: p,
            done: false,
            depth: 0,
            legal_moves_count: legal_moves_count,
            variations: vec![Variation {
                                 value: VALUE_MIN,
                                 bound: BOUND_LOWER,
                                 moves: vec![],
                             }], // TODO: Set good initial value (vec![best_move]).
            searched_nodes: 0,
            duration_millis: 0,
            ..self.status
        };
    }

    /// Stops the current search, updates the status.
    ///
    /// Does nothing if the current search is already stopped.
    pub fn stop(&mut self) {
        self.searcher.terminate_search();
        while !self.status().done {
            self.wait_status_update(Duration::from_millis(1000));
        }
    }

    /// Returns the status of the current search.
    ///
    /// **Important note:** Consecutive calls to this method will
    /// return the same unchanged result. Only after calling `search`,
    /// `stop`, or `wait_status_update`, the result returned by
    /// `status` may change.
    #[inline(always)]
    pub fn status(&self) -> &SearchStatus {
        &self.status
    }

    /// Waits for a search status update, timing out after a specified
    /// duration or earlier.
    pub fn wait_status_update(&mut self, duration: Duration) {
        self.searcher.wait_report(duration);
        while let Ok(r) = self.searcher.try_recv_report() {
            self.process_report(r)
        }
    }

    /// Stops the current search and joins the search thread.
    ///
    /// After calling `join`, no other methods on this instance should
    /// be called.
    pub fn join(&mut self) {
        self.stop();
    }

    /// A helper method. It updates the current status according to
    /// the received report message.
    fn process_report(&mut self, report: Report) {
        let duration = self.status.started_at.elapsed().unwrap();
        self.status.duration_millis = 1000 * duration.as_secs() +
                                      (duration.subsec_nanos() / 1000000) as u64;
        self.status.searched_nodes = report.searched_nodes;
        self.status.nps = 1000 * (self.status.nps + self.status.searched_nodes) /
                          (1000 + self.status.duration_millis);
        if self.status.depth < report.depth {
            self.status.depth = report.depth;
            self.status.variations = vec![extract_pv(&self.tt,
                                                     &self.status.position,
                                                     report.depth)];
        }
        self.status.done = report.done;
    }
}

/// A helper function. It extracts the primary variation (PV) from the
/// transposition table (TT) and returns it.
///
/// **Note:** Because the PV is a moving target (the search continues
/// to run in parallel), imperfections in the reported PVs are
/// unavoidable. To deal with this, we turn a blind eye if the value
/// at the root of the PV differs from the value at the leaf by no
/// more than `EPSILON`.
fn extract_pv(tt: &Tt, position: &Position, depth: u8) -> Variation {
    const EPSILON: Value = 8; // A sufficiently small value (in centipawns).

    // Extract the PV, the leaf value, the root value, and the bound
    // type from the TT.
    let mut p = position.clone();
    let mut our_turn = true;
    let mut prev_move = None;
    let mut moves = Vec::new();
    let mut leaf_value = -9999;
    let mut root_value = leaf_value;
    let mut bound = BOUND_LOWER;
    while let Some(entry) = tt.peek(p.hash()) {
        if entry.bound() != BOUND_NONE {
            // Get the value and the bound type. In half of the
            // cases the value stored in `entry` is from other
            // side's perspective.
            if our_turn {
                leaf_value = entry.value();
                bound = entry.bound();
            } else {
                leaf_value = -entry.value();
                bound = match entry.bound() {
                    BOUND_UPPER => BOUND_LOWER,
                    BOUND_LOWER => BOUND_UPPER,
                    x => x,
                };
            }

            // The values under -9999 and over 9999 may look ugly in
            // some GUIs, so we trim them.
            if leaf_value > 9999 {
                leaf_value = 9999;
                if bound == BOUND_LOWER {
                    bound = BOUND_EXACT
                }
            }
            if leaf_value < -9999 {
                leaf_value = 9999;
                if bound == BOUND_UPPER {
                    bound = BOUND_EXACT
                }
            }

            if let Some(m) = prev_move {
                // Extend the PV with the previous move.
                moves.push(m);
            } else {
                // We are at the root -- set the root value.
                root_value = leaf_value;
            }

            if moves.len() < depth as usize && (leaf_value - root_value).abs() <= EPSILON {
                if let Some(m) = p.try_move_digest(entry.move16()) {
                    if p.do_move(m) {
                        if bound == BOUND_EXACT {
                            // Extend the PV with one more move.
                            prev_move = Some(m);
                            our_turn = !our_turn;
                            continue;
                        } else {
                            // This is the last move in the PV.
                            moves.push(m);
                        }
                    }
                }
            }
        }
        break;
    }

    // Change the bound type if the leaf value in the PV differs too
    // much from the root value.
    bound = match leaf_value - root_value {
        x if x > EPSILON && bound != BOUND_UPPER => BOUND_LOWER,
        x if x < -EPSILON && bound != BOUND_LOWER => BOUND_UPPER,
        _ => bound,
    };

    Variation {
        value: root_value,
        bound: bound,
        moves: moves,
    }
}
<|endoftext|>


<commit_before>use std::{
    future::Future,
    pin::Pin,
    mem::ManuallyDrop,
    sync::{
        atomic::{AtomicBool, Ordering::Relaxed},
        mpsc::{sync_channel, Receiver, SyncSender, TryRecvError},
    },
    task::{Context, Poll, Waker},
};

#[cfg(not(feature = "testing"))]
use std::collections::HashMap as Map;

// we avoid HashMap while testing because
// it makes tests non-deterministic
#[cfg(feature = "testing")]
use std::collections::BTreeMap as Map;

use crate::*;

static ID_GEN: AtomicUsize = AtomicUsize::new(0);

/// An event that happened to a key that a subscriber is interested in.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Event {
    /// A new complete (key, value) pair
    Insert {
        /// The key that has been set
        key: IVec,
        /// The value that has been set
        value: IVec,
    },
    /// A deleted key
    Remove {
        /// The key that has been removed
        key: IVec,
    },
}

impl Event {
    /// Return the key associated with the `Event`
    pub fn key(&self) -> &IVec {
        match self {
            Event::Insert { key, .. } | Event::Remove { key } => key,
        }
    }
}

type Senders = Map<usize, (Option<Waker>, SyncSender<OneShot<Option<Event>>>)>;

/// A subscriber listening on a specified prefix
///
/// `Subscriber` implements both `Iterator<Item = Event>`
/// and `Future<Output=Option<Event>>`
///
/// # Examples
///
/// Synchronous, blocking subscriber:
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use sled::{Config, Event};
/// let config = Config::new().temporary(true);
///
/// let tree = config.open()?;
///
/// // watch all events by subscribing to the empty prefix
/// let mut subscriber = tree.watch_prefix(vec![]);
///
/// let tree_2 = tree.clone();
/// let thread = std::thread::spawn(move || {
///     tree.insert(vec![0], vec![1])
/// });
///
/// // `Subscription` implements `Iterator<Item=Event>`
/// for event in subscriber.take(1) {
///     match event {
///         Event::Insert{ key, value } => assert_eq!(key.as_ref(), &[0]),
///         Event::Remove {key } => {}
///     }
/// }
///
/// # thread.join().unwrap();
/// # Ok(())
/// # }
/// ```
/// Aynchronous, non-blocking subscriber:
///
/// `Subscription` implements `Future<Output=Option<Event>>`.
///
/// `while let Some(event) = (&mut subscriber).await { /* use it */ }`
pub struct Subscriber {
    id: usize,
    rx: ManuallyDrop<Receiver<OneShot<Option<Event>>>>,
    home: Arc<RwLock<Senders>>,
}

impl Drop for Subscriber {
    #[allow(unsafe_code)]
    fn drop(&mut self) {
        unsafe {
            ManuallyDrop::drop(&mut self.rx);
        }
        let mut w_senders = self.home.write();
        w_senders.remove(&self.id);
    }
}

impl Future for Subscriber {
    type Output = Option<Event>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        loop {
            match self.rx.try_recv() {
                Ok(mut future_rx) => {
                    #[allow(unsafe_code)]
                    let future_rx =
                        unsafe { std::pin::Pin::new_unchecked(&mut future_rx) };

                    match Future::poll(future_rx, cx) {
                        Poll::Ready(Some(event)) => {
                            return Poll::Ready(event);
                        }
                        Poll::Ready(None) => {
                            continue;
                        }
                        Poll::Pending => {
                            return Poll::Pending;
                        }
                    }
                }
                Err(TryRecvError::Empty) => break,
                Err(TryRecvError::Disconnected) => return Poll::Ready(None),
            }
        }
        let mut home = self.home.write();
        let entry = home.get_mut(&self.id).unwrap();
        entry.0 = Some(cx.waker().clone());
        Poll::Pending
    }
}

impl Iterator for Subscriber {
    type Item = Event;

    fn next(&mut self) -> Option<Event> {
        loop {
            let future_rx = self.rx.recv().ok()?;
            match future_rx.wait() {
                Some(Some(event)) => return Some(event),
                Some(None) => return None,
                None => continue,
            }
        }
    }
}

#[derive(Debug, Default)]
pub(crate) struct Subscribers {
    watched: RwLock<BTreeMap<Vec<u8>, Arc<RwLock<Senders>>>>,
    ever_used: AtomicBool,
}

impl Drop for Subscribers {
    fn drop(&mut self) {
        let watched = self.watched.read();

        for senders in watched.values() {
            let senders =
                std::mem::replace(&mut *senders.write(), Map::default());
            for (_, (waker, sender)) in senders {
                drop(sender);
                if let Some(waker) = waker {
                    waker.wake();
                }
            }
        }
    }
}

impl Subscribers {
    pub(crate) fn register(&self, prefix: &[u8]) -> Subscriber {
        self.ever_used.store(true, Relaxed);
        let r_mu = {
            let r_mu = self.watched.read();
            if r_mu.contains_key(prefix) {
                r_mu
            } else {
                drop(r_mu);
                let mut w_mu = self.watched.write();
                if !w_mu.contains_key(prefix) {
                    let old = w_mu.insert(
                        prefix.to_vec(),
                        Arc::new(RwLock::new(Map::default())),
                    );
                    assert!(old.is_none());
                }
                drop(w_mu);
                self.watched.read()
            }
        };

        let (tx, rx) = sync_channel(1024);

        let arc_senders = &r_mu[prefix];
        let mut w_senders = arc_senders.write();

        let id = ID_GEN.fetch_add(1, Relaxed);

        w_senders.insert(id, (None, tx));

        Subscriber { id, rx: ManuallyDrop::new(rx), home: arc_senders.clone() }
    }

    pub(crate) fn reserve<R: AsRef<[u8]>>(
        &self,
        key: R,
    ) -> Option<ReservedBroadcast> {
        if !self.ever_used.load(Relaxed) {
            return None;
        }

        let r_mu = self.watched.read();
        let prefixes = r_mu.iter().filter(|(k, _)| key.as_ref().starts_with(k));

        let mut subscribers = vec![];

        for (_, subs_rwl) in prefixes {
            let subs = subs_rwl.read();

            for (_id, (waker, sender)) in subs.iter() {
                let (tx, rx) = OneShot::pair();
                if sender.send(rx).is_err() {
                    continue;
                }
                subscribers.push((waker.clone(), tx));
            }
        }

        if subscribers.is_empty() {
            None
        } else {
            Some(ReservedBroadcast { subscribers })
        }
    }
}

pub(crate) struct ReservedBroadcast {
    subscribers: Vec<(Option<Waker>, OneShotFiller<Option<Event>>)>,
}

impl ReservedBroadcast {
    pub fn complete(self, event: &Event) {
        let iter = self.subscribers.into_iter();

        for (waker, tx) in iter {
            tx.fill(Some(event.clone()));
            if let Some(waker) = waker {
                waker.wake();
            }
        }
    }
}

#[test]
fn basic_subscriber() {
    let subs = Subscribers::default();

    let mut s2 = subs.register(&[0]);
    let mut s3 = subs.register(&[0, 1]);
    let mut s4 = subs.register(&[1, 2]);

    let r1 = subs.reserve(b"awft");
    assert!(r1.is_none());

    let mut s1 = subs.register(&[]);

    let k2: IVec = vec![].into();
    let r2 = subs.reserve(&k2).unwrap();
    r2.complete(&Event::Insert {
        key: k2.clone(),
        value: IVec::from(k2.clone()),
    });

    let k3: IVec = vec![0].into();
    let r3 = subs.reserve(&k3).unwrap();
    r3.complete(&Event::Insert {
        key: k3.clone(),
        value: IVec::from(k3.clone()),
    });

    let k4: IVec = vec![0, 1].into();
    let r4 = subs.reserve(&k4).unwrap();
    r4.complete(&Event::Remove { key: k4.clone() });

    let k5: IVec = vec![0, 1, 2].into();
    let r5 = subs.reserve(&k5).unwrap();
    r5.complete(&Event::Insert {
        key: k5.clone(),
        value: IVec::from(k5.clone()),
    });

    let k6: IVec = vec![1, 1, 2].into();
    let r6 = subs.reserve(&k6).unwrap();
    r6.complete(&Event::Remove { key: k6.clone() });

    let k7: IVec = vec![1, 1, 2].into();
    let r7 = subs.reserve(&k7).unwrap();
    drop(r7);

    let k8: IVec = vec![1, 2, 2].into();
    let r8 = subs.reserve(&k8).unwrap();
    r8.complete(&Event::Insert {
        key: k8.clone(),
        value: IVec::from(k8.clone()),
    });

    assert_eq!(s1.next().unwrap().key(), &*k2);
    assert_eq!(s1.next().unwrap().key(), &*k3);
    assert_eq!(s1.next().unwrap().key(), &*k4);
    assert_eq!(s1.next().unwrap().key(), &*k5);
    assert_eq!(s1.next().unwrap().key(), &*k6);
    assert_eq!(s1.next().unwrap().key(), &*k8);

    assert_eq!(s2.next().unwrap().key(), &*k3);
    assert_eq!(s2.next().unwrap().key(), &*k4);
    assert_eq!(s2.next().unwrap().key(), &*k5);

    assert_eq!(s3.next().unwrap().key(), &*k4);
    assert_eq!(s3.next().unwrap().key(), &*k5);

    assert_eq!(s4.next().unwrap().key(), &*k8);
}
<commit_msg>Revert "fix destructor deadlock"<commit_after>use std::{
    future::Future,
    pin::Pin,
    sync::{
        atomic::{AtomicBool, Ordering::Relaxed},
        mpsc::{sync_channel, Receiver, SyncSender, TryRecvError},
    },
    task::{Context, Poll, Waker},
};

#[cfg(not(feature = "testing"))]
use std::collections::HashMap as Map;

// we avoid HashMap while testing because
// it makes tests non-deterministic
#[cfg(feature = "testing")]
use std::collections::BTreeMap as Map;

use crate::*;

static ID_GEN: AtomicUsize = AtomicUsize::new(0);

/// An event that happened to a key that a subscriber is interested in.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Event {
    /// A new complete (key, value) pair
    Insert {
        /// The key that has been set
        key: IVec,
        /// The value that has been set
        value: IVec,
    },
    /// A deleted key
    Remove {
        /// The key that has been removed
        key: IVec,
    },
}

impl Event {
    /// Return the key associated with the `Event`
    pub fn key(&self) -> &IVec {
        match self {
            Event::Insert { key, .. } | Event::Remove { key } => key,
        }
    }
}

type Senders = Map<usize, (Option<Waker>, SyncSender<OneShot<Option<Event>>>)>;

/// A subscriber listening on a specified prefix
///
/// `Subscriber` implements both `Iterator<Item = Event>`
/// and `Future<Output=Option<Event>>`
///
/// # Examples
///
/// Synchronous, blocking subscriber:
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use sled::{Config, Event};
/// let config = Config::new().temporary(true);
///
/// let tree = config.open()?;
///
/// // watch all events by subscribing to the empty prefix
/// let mut subscriber = tree.watch_prefix(vec![]);
///
/// let tree_2 = tree.clone();
/// let thread = std::thread::spawn(move || {
///     tree.insert(vec![0], vec![1])
/// });
///
/// // `Subscription` implements `Iterator<Item=Event>`
/// for event in subscriber.take(1) {
///     match event {
///         Event::Insert{ key, value } => assert_eq!(key.as_ref(), &[0]),
///         Event::Remove {key } => {}
///     }
/// }
///
/// # thread.join().unwrap();
/// # Ok(())
/// # }
/// ```
/// Aynchronous, non-blocking subscriber:
///
/// `Subscription` implements `Future<Output=Option<Event>>`.
///
/// `while let Some(event) = (&mut subscriber).await { /* use it */ }`
pub struct Subscriber {
    id: usize,
    rx: Receiver<OneShot<Option<Event>>>,
    home: Arc<RwLock<Senders>>,
}

impl Drop for Subscriber {
    fn drop(&mut self) {
        let mut w_senders = self.home.write();
        w_senders.remove(&self.id);
    }
}

impl Future for Subscriber {
    type Output = Option<Event>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        loop {
            match self.rx.try_recv() {
                Ok(mut future_rx) => {
                    #[allow(unsafe_code)]
                    let future_rx =
                        unsafe { std::pin::Pin::new_unchecked(&mut future_rx) };

                    match Future::poll(future_rx, cx) {
                        Poll::Ready(Some(event)) => {
                            return Poll::Ready(event);
                        }
                        Poll::Ready(None) => {
                            continue;
                        }
                        Poll::Pending => {
                            return Poll::Pending;
                        }
                    }
                }
                Err(TryRecvError::Empty) => break,
                Err(TryRecvError::Disconnected) => return Poll::Ready(None),
            }
        }
        let mut home = self.home.write();
        let entry = home.get_mut(&self.id).unwrap();
        entry.0 = Some(cx.waker().clone());
        Poll::Pending
    }
}

impl Iterator for Subscriber {
    type Item = Event;

    fn next(&mut self) -> Option<Event> {
        loop {
            let future_rx = self.rx.recv().ok()?;
            match future_rx.wait() {
                Some(Some(event)) => return Some(event),
                Some(None) => return None,
                None => continue,
            }
        }
    }
}

#[derive(Debug, Default)]
pub(crate) struct Subscribers {
    watched: RwLock<BTreeMap<Vec<u8>, Arc<RwLock<Senders>>>>,
    ever_used: AtomicBool,
}

impl Drop for Subscribers {
    fn drop(&mut self) {
        let watched = self.watched.read();

        for senders in watched.values() {
            let senders =
                std::mem::replace(&mut *senders.write(), Map::default());
            for (_, (waker, sender)) in senders {
                drop(sender);
                if let Some(waker) = waker {
                    waker.wake();
                }
            }
        }
    }
}

impl Subscribers {
    pub(crate) fn register(&self, prefix: &[u8]) -> Subscriber {
        self.ever_used.store(true, Relaxed);
        let r_mu = {
            let r_mu = self.watched.read();
            if r_mu.contains_key(prefix) {
                r_mu
            } else {
                drop(r_mu);
                let mut w_mu = self.watched.write();
                if !w_mu.contains_key(prefix) {
                    let old = w_mu.insert(
                        prefix.to_vec(),
                        Arc::new(RwLock::new(Map::default())),
                    );
                    assert!(old.is_none());
                }
                drop(w_mu);
                self.watched.read()
            }
        };

        let (tx, rx) = sync_channel(1024);

        let arc_senders = &r_mu[prefix];
        let mut w_senders = arc_senders.write();

        let id = ID_GEN.fetch_add(1, Relaxed);

        w_senders.insert(id, (None, tx));

        Subscriber { id, rx, home: arc_senders.clone() }
    }

    pub(crate) fn reserve<R: AsRef<[u8]>>(
        &self,
        key: R,
    ) -> Option<ReservedBroadcast> {
        if !self.ever_used.load(Relaxed) {
            return None;
        }

        let r_mu = self.watched.read();
        let prefixes = r_mu.iter().filter(|(k, _)| key.as_ref().starts_with(k));

        let mut subscribers = vec![];

        for (_, subs_rwl) in prefixes {
            let subs = subs_rwl.read();

            for (_id, (waker, sender)) in subs.iter() {
                let (tx, rx) = OneShot::pair();
                if sender.send(rx).is_err() {
                    continue;
                }
                subscribers.push((waker.clone(), tx));
            }
        }

        if subscribers.is_empty() {
            None
        } else {
            Some(ReservedBroadcast { subscribers })
        }
    }
}

pub(crate) struct ReservedBroadcast {
    subscribers: Vec<(Option<Waker>, OneShotFiller<Option<Event>>)>,
}

impl ReservedBroadcast {
    pub fn complete(self, event: &Event) {
        let iter = self.subscribers.into_iter();

        for (waker, tx) in iter {
            tx.fill(Some(event.clone()));
            if let Some(waker) = waker {
                waker.wake();
            }
        }
    }
}

#[test]
fn basic_subscriber() {
    let subs = Subscribers::default();

    let mut s2 = subs.register(&[0]);
    let mut s3 = subs.register(&[0, 1]);
    let mut s4 = subs.register(&[1, 2]);

    let r1 = subs.reserve(b"awft");
    assert!(r1.is_none());

    let mut s1 = subs.register(&[]);

    let k2: IVec = vec![].into();
    let r2 = subs.reserve(&k2).unwrap();
    r2.complete(&Event::Insert {
        key: k2.clone(),
        value: IVec::from(k2.clone()),
    });

    let k3: IVec = vec![0].into();
    let r3 = subs.reserve(&k3).unwrap();
    r3.complete(&Event::Insert {
        key: k3.clone(),
        value: IVec::from(k3.clone()),
    });

    let k4: IVec = vec![0, 1].into();
    let r4 = subs.reserve(&k4).unwrap();
    r4.complete(&Event::Remove { key: k4.clone() });

    let k5: IVec = vec![0, 1, 2].into();
    let r5 = subs.reserve(&k5).unwrap();
    r5.complete(&Event::Insert {
        key: k5.clone(),
        value: IVec::from(k5.clone()),
    });

    let k6: IVec = vec![1, 1, 2].into();
    let r6 = subs.reserve(&k6).unwrap();
    r6.complete(&Event::Remove { key: k6.clone() });

    let k7: IVec = vec![1, 1, 2].into();
    let r7 = subs.reserve(&k7).unwrap();
    drop(r7);

    let k8: IVec = vec![1, 2, 2].into();
    let r8 = subs.reserve(&k8).unwrap();
    r8.complete(&Event::Insert {
        key: k8.clone(),
        value: IVec::from(k8.clone()),
    });

    assert_eq!(s1.next().unwrap().key(), &*k2);
    assert_eq!(s1.next().unwrap().key(), &*k3);
    assert_eq!(s1.next().unwrap().key(), &*k4);
    assert_eq!(s1.next().unwrap().key(), &*k5);
    assert_eq!(s1.next().unwrap().key(), &*k6);
    assert_eq!(s1.next().unwrap().key(), &*k8);

    assert_eq!(s2.next().unwrap().key(), &*k3);
    assert_eq!(s2.next().unwrap().key(), &*k4);
    assert_eq!(s2.next().unwrap().key(), &*k5);

    assert_eq!(s3.next().unwrap().key(), &*k4);
    assert_eq!(s3.next().unwrap().key(), &*k5);

    assert_eq!(s4.next().unwrap().key(), &*k8);
}
<|endoftext|>


<commit_before>extern crate time;

use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};

const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";

impl ToSql for time::Timespec {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        let time_string = time::at_utc(*self)
            .strftime(SQLITE_DATETIME_FMT)
            .unwrap()
            .to_string();
        Ok(ToSqlOutput::from(time_string))
    }
}

impl FromSql for time::Timespec {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        value
            .as_str()
            .and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
                          Ok(tm) => Ok(tm.to_timespec()),
                          Err(err) => Err(FromSqlError::Other(Box::new(err))),
                      })
    }
}

#[cfg(test)]
mod test {
    use Connection;
    use super::time;

    fn checked_memory_handle() -> Connection {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
            .unwrap();
        db
    }

    #[test]
    fn test_timespec() {
        let db = checked_memory_handle();

        let ts = time::Timespec {
            sec: 10_000,
            nsec: 0,
        };
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
            .unwrap();

        let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
            .unwrap();
        assert_eq!(from, ts);
    }
}
<commit_msg>Fixed loss of time information during string conversion<commit_after>extern crate time;

use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};

const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%f %z";

impl ToSql for time::Timespec {
    fn to_sql(&self) -> Result<ToSqlOutput> {
        let time_string = time::at_utc(*self)
            .strftime(SQLITE_DATETIME_FMT)
            .unwrap()
            .to_string();
        Ok(ToSqlOutput::from(time_string))
    }
}

impl FromSql for time::Timespec {
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
        value
            .as_str()
            .and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
                          Ok(tm) => Ok(tm.to_timespec()),
                          Err(err) => Err(FromSqlError::Other(Box::new(err))),
                      })
    }
}

#[cfg(test)]
mod test {
    use Connection;
    use super::time;

    fn checked_memory_handle() -> Connection {
        let db = Connection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
            .unwrap();
        db
    }

    #[test]
    fn test_timespec() {
        let db = checked_memory_handle();

        let ts = time::Timespec {
            sec: 10_000,
            nsec: 0,
        };
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
            .unwrap();

        let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
            .unwrap();
        assert_eq!(from, ts);
    }
}
<|endoftext|>