<commit_before>//! Constant-time comparison of fixed-size vecs
use ffi;

/// `verify_16()` returns `true` if `x[0]`, `x[1]`, ..., `x[15]` are the
/// same as `y[0]`, `y[1]`, ..., `y[15]`. Otherwise it returns `false`.
///
/// This functions is safe to use for secrets `x[0]`, `x[1]`, ..., `x[15]`,
/// `y[0]`, `y[1]`, ..., `y[15]`. The time taken by `verify_16` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[15]`, `y[0]`, `y[1]`, ..., `y[15]`.
/// In contrast, the standard C comparison function `memcmp(x,y,16)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_16(x: &[u8; 16], y: &[u8; 16]) -> bool {
    unsafe {
        ffi::crypto_verify_16(x, y) == 0
    }
}

/// `verify_32()` returns true if `x[0]`, `x[1]`, ..., `x[31]` are the
/// same as `y[0]`, `y[1]`, ..., `y[31]`. Otherwise it returns `false`.
///
/// This functions is safe to use for secrets `x[0]`, `x[1]`, ..., `x[31]`,
/// `y[0]`, `y[1]`, ..., `y[31]`. The time taken by `verify_32` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[31]`, `y[0]`, `y[1]`, ..., `y[31]`.
/// In contrast, the standard C comparison function `memcmp(x,y,32)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_32(x: &[u8; 32], y: &[u8; 32]) -> bool {
    unsafe {
        ffi::crypto_verify_32(x, y) == 0
    }
}

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

    #[test]
    fn test_verify_16() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 16];
            let mut y = [0; 16];
            assert!(verify_16(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_16(&x, &y))
            } else {
                assert!(!verify_16(&x, &y))
            }
        }
    }

    #[test]
    fn test_verify_32() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 32];
            let mut y = [0; 32];
            assert!(verify_32(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_32(&x, &y))
            } else {
                assert!(!verify_32(&x, &y))
            }
        }
    }
}
<commit_msg>Add verify_64<commit_after>//! Constant-time comparison of fixed-size vecs
use ffi;

/// `verify_16()` returns `true` if `x[0]`, `x[1]`, ..., `x[15]` are the
/// same as `y[0]`, `y[1]`, ..., `y[15]`. Otherwise it returns `false`.
///
/// This functions is safe to use for secrets `x[0]`, `x[1]`, ..., `x[15]`,
/// `y[0]`, `y[1]`, ..., `y[15]`. The time taken by `verify_16` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[15]`, `y[0]`, `y[1]`, ..., `y[15]`.
/// In contrast, the standard C comparison function `memcmp(x,y,16)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_16(x: &[u8; 16], y: &[u8; 16]) -> bool {
    unsafe {
        ffi::crypto_verify_16(x, y) == 0
    }
}

/// `verify_32()` returns true if `x[0]`, `x[1]`, ..., `x[31]` are the
/// same as `y[0]`, `y[1]`, ..., `y[31]`. Otherwise it returns `false`.
///
/// This functions is safe to use for secrets `x[0]`, `x[1]`, ..., `x[31]`,
/// `y[0]`, `y[1]`, ..., `y[31]`. The time taken by `verify_32` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[31]`, `y[0]`, `y[1]`, ..., `y[31]`.
/// In contrast, the standard C comparison function `memcmp(x,y,32)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_32(x: &[u8; 32], y: &[u8; 32]) -> bool {
    unsafe {
        ffi::crypto_verify_32(x, y) == 0
    }
}

/// `verify_64()` returns true if `x[0]`, `x[1]`, ..., `x[63]` are the
/// same as `y[0]`, `y[1]`, ..., `y[63]`. Otherwise it returns `false`.
///
/// This functions is safe to use for secrets `x[0]`, `x[1]`, ..., `x[63]`,
/// `y[0]`, `y[1]`, ..., `y[63]`. The time taken by `verify_64` is independent
/// of the contents of `x[0]`, `x[1]`, ..., `x[63]`, `y[0]`, `y[1]`, ..., `y[63]`.
/// In contrast, the standard C comparison function `memcmp(x,y,64)` takes time
/// that depends on the longest matching prefix of `x` and `y`, often allowing easy
/// timing attacks.
pub fn verify_64(x: &[u8; 64], y: &[u8; 64]) -> bool {
    unsafe {
        ffi::crypto_verify_64(x, y) == 0
    }
}

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

    #[test]
    fn test_verify_16() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 16];
            let mut y = [0; 16];
            assert!(verify_16(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_16(&x, &y))
            } else {
                assert!(!verify_16(&x, &y))
            }
        }
    }

    #[test]
    fn test_verify_32() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 32];
            let mut y = [0; 32];
            assert!(verify_32(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x == y {
                assert!(verify_32(&x, &y))
            } else {
                assert!(!verify_32(&x, &y))
            }
        }
    }

    #[test]
    fn test_verify_64() {
        use randombytes::randombytes_into;

        for _ in (0usize..256) {
            let mut x = [0; 64];
            let mut y = [0; 64];
            assert!(verify_64(&x, &y));
            randombytes_into(&mut x);
            randombytes_into(&mut y);
            if x[..] == y[..] {
                assert!(verify_64(&x, &y))
            } else {
                assert!(!verify_64(&x, &y))
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate inotify;
extern crate libc;

use inotify::INotify;
use inotify::wrapper::Event;
use inotify::wrapper::Watch;
use inotify::ffi::*;

use std::collections::HashMap;
use std::path::PathBuf;

pub struct FilesWatcher {
    inotify: INotify,
    watches: HashMap<Watch, PathBuf>
}

impl FilesWatcher {
    pub fn new() -> FilesWatcher {
        let inotify = INotify::init().unwrap();

        FilesWatcher {
            inotify: inotify,
            watches: HashMap::new()
        }
    }

    pub fn add_file(&mut self, path: PathBuf) {
        let watch_id = self.inotify.add_watch(&path, IN_MODIFY | IN_DELETE).unwrap();
        self.watches.insert(watch_id, path);
    }

    pub fn wait_for_events(&mut self) -> Vec<EventPath> {
        let events = self.inotify.wait_for_events().unwrap();

        let mut event_paths: Vec<EventPath> = vec![];
        for event in events.iter() {
            let path: PathBuf = self.watches.get(&event.wd).unwrap().clone();
            event_paths.push(EventPath::new(path, event.clone()));
        }

        event_paths
    }

    pub fn close(&mut self) {
        self.inotify.close().unwrap();
    }
}

pub struct EventPath {
    event: Event,
    path: PathBuf
}

impl EventPath {
    fn new(path: PathBuf, event: Event) -> EventPath {
        EventPath {
            event: event,
            path: path
        }
    }
}

#[cfg(test)]
mod test {
    extern crate rand;

    use super::*;

    use self::rand::{thread_rng, Rng};
    use std::env::temp_dir;
    use std::fs::File;
    use std::fs::remove_file;
    use std::io::Write;
    use std::path::Path;
    use std::path::PathBuf;

    #[test]
    fn watch_a_single_file() {
        let (path, mut file) = create_temp_file();
        let filepath = path.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path);

        write_to(&mut file);

        {
            let event_paths = fw.wait_for_events();
            assert_eq!(1, event_paths.len());
            for event_path in event_paths.iter() {
                assert_eq!(filepath, event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    #[test]
    fn watch_two_files() {
        let (path1, mut file1) = create_temp_file();
        let (path2, mut file2) = create_temp_file();

        let filepath1 = path1.clone();
        let filepath2 = path2.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path1);
        fw.add_file(path2);

        write_to(&mut file1);
        write_to(&mut file2);

        {
            let event_paths = fw.wait_for_events();
            assert_eq!(2, event_paths.len());
            for event_path in event_paths.iter() {
                assert!(filepath1 == event_path.path || filepath2 == event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    fn create_temp_file() -> (PathBuf, File) {
        let rand_part: String = thread_rng()
            .gen_ascii_chars()
            .take(8)
            .collect();

        let filename = "eagleeye-test-".to_string() + &rand_part;
        let path = temp_dir().join(filename);
        let file = File::create(&path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
        let path_buf = PathBuf::from(path);

        (path_buf, file)
    }

    fn remove_temp_file(path: &Path) {
        remove_file(path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
    }

    fn write_to(file: &mut File) {
        file
            .write(b"This should trigger an inotify event.")
            .unwrap_or_else(|error|
                panic!("Failed to write to file: {}", error)
            );
    }

}

<commit_msg>Improve error handling<commit_after>extern crate inotify;
extern crate libc;

use inotify::INotify;
use inotify::wrapper::Event;
use inotify::wrapper::Watch;
use inotify::ffi::*;

use std::collections::HashMap;
use std::io;
use std::path::PathBuf;

pub struct FilesWatcher {
    inotify: INotify,
    watches: HashMap<Watch, PathBuf>
}

impl FilesWatcher {
    pub fn new() -> FilesWatcher {
        let inotify = INotify::init().ok().expect(
            "Fatal Error: Could not initialize inotify."
        );

        FilesWatcher {
            inotify: inotify,
            watches: HashMap::new()
        }
    }

    pub fn add_file(&mut self, path: PathBuf) {
        let watch_id = self.inotify.add_watch(&path, IN_MODIFY | IN_DELETE);

        if watch_id.is_ok() {
           self.watches.insert(watch_id.unwrap(), path);
        } else {
            println!("Error adding watch for file: {:?}", watch_id.err());
        }
    }

    pub fn wait_for_events(&mut self) -> io::Result<Vec<EventPath>> {
        let events = try!(self.inotify.wait_for_events());

        let mut event_paths: Vec<EventPath> = vec![];
        for event in events.iter() {
            let path = self.watches.get(&event.wd);
            if path.is_some() {
                event_paths.push(EventPath::new(
                    path.unwrap().clone(),
                    event.clone()
                ));
            } else {
                println!("Error: no path for watch: {:?}", event.wd);
            }
        }

        Ok(event_paths)
    }

    pub fn close(&mut self) {
        self.inotify.close().ok().expect(
            "Fatal Error: Could not close inotify."
        );
    }
}

pub struct EventPath {
    event: Event,
    path: PathBuf
}

impl EventPath {
    fn new(path: PathBuf, event: Event) -> EventPath {
        EventPath {
            event: event,
            path: path
        }
    }
}

#[cfg(test)]
mod test {
    extern crate rand;

    use super::*;

    use self::rand::{thread_rng, Rng};
    use std::env::temp_dir;
    use std::fs::File;
    use std::fs::remove_file;
    use std::io::Write;
    use std::path::Path;
    use std::path::PathBuf;

    #[test]
    fn watch_a_single_file() {
        let (path, mut file) = create_temp_file();
        let filepath = path.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path);

        write_to(&mut file);

        {
            let event_paths = fw.wait_for_events().unwrap();
            assert_eq!(1, event_paths.len());
            for event_path in event_paths.iter() {
                assert_eq!(filepath, event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    #[test]
    fn watch_two_files() {
        let (path1, mut file1) = create_temp_file();
        let (path2, mut file2) = create_temp_file();

        let filepath1 = path1.clone();
        let filepath2 = path2.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path1);
        fw.add_file(path2);

        write_to(&mut file1);
        write_to(&mut file2);

        {
            let event_paths = fw.wait_for_events().unwrap();
            assert_eq!(2, event_paths.len());
            for event_path in event_paths.iter() {
                assert!(filepath1 == event_path.path || filepath2 == event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    fn create_temp_file() -> (PathBuf, File) {
        let rand_part: String = thread_rng()
            .gen_ascii_chars()
            .take(8)
            .collect();

        let filename = "eagleeye-test-".to_string() + &rand_part;
        let path = temp_dir().join(filename);
        let file = File::create(&path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
        let path_buf = PathBuf::from(path);

        (path_buf, file)
    }

    fn remove_temp_file(path: &Path) {
        remove_file(path).unwrap_or_else(|error|
            panic!("Failed to create temporary file: {}", error)
        );
    }

    fn write_to(file: &mut File) {
        file
            .write(b"This should trigger an inotify event.")
            .unwrap_or_else(|error|
                panic!("Failed to write to file: {}", error)
            );
    }

}

<|endoftext|>


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

use crate::firmware::metadata_value::MetadataValue;

use easy_process;
use slog::slog_error;
use slog_scope::error;
use std::{path::Path, str::FromStr};
use walkdir::WalkDir;

pub(crate) fn run_hook(path: &Path) -> Result<String, failure::Error> {
    if !path.exists() {
        return Ok("".into());
    }

    Ok(run_script(path.to_str().expect("Invalid path for hook"))?)
}

pub(crate) fn run_hooks_from_dir(path: &Path) -> Result<MetadataValue, failure::Error> {
    let mut outputs: Vec<String> = Vec::new();
    for entry in WalkDir::new(path)
        .follow_links(true)
        .min_depth(1)
        .max_depth(1)
    {
        outputs.push(run_hook(entry?.path())?);
    }

    Ok(MetadataValue::from_str(&outputs.join("\n"))?)
}

pub(crate) fn run_script(cmd: &str) -> Result<String, failure::Error> {
    let output = easy_process::run(cmd)?;
    if !output.stderr.is_empty() {
        output
            .stderr
            .lines()
            .for_each(|err| error!("{} (stderr): {}", cmd, err))
    }

    Ok(output.stdout.trim().into())
}
<commit_msg>firmware: hook: Add context when running script<commit_after>// Copyright (C) 2017, 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use crate::firmware::metadata_value::MetadataValue;

use easy_process;
use failure::ResultExt;
use slog::slog_error;
use slog_scope::error;
use std::{path::Path, str::FromStr};
use walkdir::WalkDir;

pub(crate) fn run_hook(path: &Path) -> Result<String, failure::Error> {
    if !path.exists() {
        return Ok("".into());
    }

    Ok(run_script(path.to_str().expect("Invalid path for hook"))?)
}

pub(crate) fn run_hooks_from_dir(path: &Path) -> Result<MetadataValue, failure::Error> {
    let mut outputs: Vec<String> = Vec::new();
    for entry in WalkDir::new(path)
        .follow_links(true)
        .min_depth(1)
        .max_depth(1)
    {
        outputs.push(run_hook(entry?.path())?);
    }

    Ok(MetadataValue::from_str(&outputs.join("\n"))?)
}

pub(crate) fn run_script(cmd: &str) -> Result<String, failure::Error> {
    let output = easy_process::run(cmd).context(format!("Running {:?}", cmd))?;
    if !output.stderr.is_empty() {
        output
            .stderr
            .lines()
            .for_each(|err| error!("{} (stderr): {}", cmd, err))
    }

    Ok(output.stdout.trim().into())
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::ops::Deref;
use std::sync::{Arc, Mutex, Weak};
use std::thread::JoinHandle;
use std::time::Duration;

use crate::{Executor, Inventory, Searcher, SearcherGeneration, TantivyError};

pub const GC_INTERVAL: Duration = Duration::from_secs(1);

/// `Warmer` can be used to maintain segment-level state e.g. caches.
///
/// They must be registered with the [IndexReaderBuilder].
pub trait Warmer: Sync + Send {
    /// Perform any warming work using the provided [Searcher].
    fn warm(&self, searcher: &Searcher) -> crate::Result<()>;

    /// Discards internal state for any [SearcherGeneration] not provided.
    fn garbage_collect(&self, live_generations: &[&SearcherGeneration]);
}

/// Warming-related state with interior mutability.
#[derive(Clone)]
pub(crate) struct WarmingState(Arc<Mutex<WarmingStateInner>>);

impl WarmingState {
    pub fn new(
        num_warming_threads: usize,
        warmers: Vec<Weak<dyn Warmer>>,
        searcher_generation_inventory: Inventory<SearcherGeneration>,
    ) -> crate::Result<Self> {
        Ok(Self(Arc::new(Mutex::new(WarmingStateInner {
            num_warming_threads,
            warmers,
            gc_thread: None,
            warmed_generation_ids: Default::default(),
            searcher_generation_inventory,
        }))))
    }

    /// Start tracking a new generation of [Searcher], and [Warmer::warm] it if there are active
    /// warmers.
    ///
    /// A background GC thread for [Warmer::garbage_collect] calls is uniquely created if there are
    /// active warmers.
    pub fn warm_new_searcher_generation(&self, searcher: &Searcher) -> crate::Result<()> {
        self.0
            .lock()
            .unwrap()
            .warm_new_searcher_generation(searcher, &self.0)
    }

    #[cfg(test)]
    fn gc_maybe(&self) -> bool {
        self.0.lock().unwrap().gc_maybe()
    }
}

struct WarmingStateInner {
    num_warming_threads: usize,
    warmers: Vec<Weak<dyn Warmer>>,
    gc_thread: Option<JoinHandle<()>>,
    // Contains all generations that have been warmed up.
    // This list is used to avoid triggers the individual Warmer GCs
    // if no warmed generation needs to be collected.
    warmed_generation_ids: HashSet<u64>,
    searcher_generation_inventory: Inventory<SearcherGeneration>,
}

impl WarmingStateInner {
    /// Start tracking provided searcher as an exemplar of a new generation.
    /// If there are active warmers, warm them with the provided searcher, and kick background GC
    /// thread if it has not yet been kicked. Otherwise, prune state for dropped searcher
    /// generations inline.
    fn warm_new_searcher_generation(
        &mut self,
        searcher: &Searcher,
        this: &Arc<Mutex<Self>>,
    ) -> crate::Result<()> {
        let warmers = self.pruned_warmers();
        // Avoid threads (warming as well as background GC) if there are no warmers
        if warmers.is_empty() {
            return Ok(());
        }
        self.start_gc_thread_maybe(this)?;
        self.warmed_generation_ids
            .insert(searcher.generation().generation_id());
        warming_executor(self.num_warming_threads.min(warmers.len()))?
            .map(|warmer| warmer.warm(searcher), warmers.into_iter())?;
        Ok(())
    }

    /// Attempt to upgrade the weak Warmer references, pruning those which cannot be upgraded.
    /// Return the strong references.
    fn pruned_warmers(&mut self) -> Vec<Arc<dyn Warmer>> {
        let strong_warmers = self
            .warmers
            .iter()
            .flat_map(|weak_warmer| weak_warmer.upgrade())
            .collect::<Vec<_>>();
        self.warmers = strong_warmers.iter().map(Arc::downgrade).collect();
        strong_warmers
    }

    /// [Warmer::garbage_collect] active warmers if some searcher generation is observed to have
    /// been dropped.
    fn gc_maybe(&mut self) -> bool {
        let live_generations = self.searcher_generation_inventory.list();
        let live_generation_ids: HashSet<u64> = live_generations
            .iter()
            .map(|searcher_generation| searcher_generation.generation_id())
            .collect();
        let gc_not_required = self
            .warmed_generation_ids
            .iter()
            .all(|warmed_up_generation| live_generation_ids.contains(warmed_up_generation));
        if gc_not_required {
            return false;
        }
        let live_generation_refs = live_generations
            .iter()
            .map(Deref::deref)
            .collect::<Vec<_>>();
        for warmer in self.pruned_warmers() {
            warmer.garbage_collect(&live_generation_refs);
        }
        self.warmed_generation_ids = live_generation_ids;
        true
    }

    /// Start GC thread if one has not already been started.
    fn start_gc_thread_maybe(&mut self, this: &Arc<Mutex<Self>>) -> crate::Result<bool> {
        if self.gc_thread.is_some() {
            return Ok(false);
        }
        let weak_inner = Arc::downgrade(this);
        let handle = std::thread::Builder::new()
            .name("tantivy-warm-gc".to_owned())
            .spawn(|| Self::gc_loop(weak_inner))
            .map_err(|_| {
                TantivyError::SystemError("Failed to spawn warmer GC thread".to_owned())
            })?;
        self.gc_thread = Some(handle);
        Ok(true)
    }

    /// Every [GC_INTERVAL] attempt to GC, with panics caught and logged using
    /// [std::panic::catch_unwind].
    fn gc_loop(inner: Weak<Mutex<WarmingStateInner>>) {
        for _ in crossbeam::channel::tick(GC_INTERVAL) {
            if let Some(inner) = inner.upgrade() {
                // rely on deterministic gc in tests
                #[cfg(not(test))]
                if let Err(err) = std::panic::catch_unwind(|| inner.lock().unwrap().gc_maybe()) {
                    error!("Panic in Warmer GC {:?}", err);
                }
                // avoid unused var warning in tests
                #[cfg(test)]
                drop(inner);
            }
        }
    }
}

fn warming_executor(num_threads: usize) -> crate::Result<Executor> {
    if num_threads <= 1 {
        Ok(Executor::single_thread())
    } else {
        Executor::multi_thread(num_threads, "tantivy-warm-")
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashSet;
    use std::sync::atomic::{self, AtomicUsize};
    use std::sync::{Arc, RwLock, Weak};

    use super::Warmer;
    use crate::core::searcher::SearcherGeneration;
    use crate::directory::RamDirectory;
    use crate::schema::{Schema, INDEXED};
    use crate::{Index, IndexSettings, ReloadPolicy, Searcher, SegmentId};

    #[derive(Default)]
    struct TestWarmer {
        active_segment_ids: RwLock<HashSet<SegmentId>>,
        warm_calls: AtomicUsize,
        gc_calls: AtomicUsize,
    }

    impl TestWarmer {
        fn live_segment_ids(&self) -> HashSet<SegmentId> {
            self.active_segment_ids.read().unwrap().clone()
        }

        fn warm_calls(&self) -> usize {
            self.warm_calls.load(atomic::Ordering::Acquire)
        }

        fn gc_calls(&self) -> usize {
            self.gc_calls.load(atomic::Ordering::Acquire)
        }

        fn verify(
            &self,
            expected_warm_calls: usize,
            expected_gc_calls: usize,
            expected_segment_ids: HashSet<SegmentId>,
        ) {
            assert_eq!(self.warm_calls(), expected_warm_calls);
            assert_eq!(self.gc_calls(), expected_gc_calls);
            assert_eq!(self.live_segment_ids(), expected_segment_ids);
        }
    }

    impl Warmer for TestWarmer {
        fn warm(&self, searcher: &crate::Searcher) -> crate::Result<()> {
            self.warm_calls.fetch_add(1, atomic::Ordering::SeqCst);
            for reader in searcher.segment_readers() {
                self.active_segment_ids
                    .write()
                    .unwrap()
                    .insert(reader.segment_id());
            }
            Ok(())
        }

        fn garbage_collect(&self, live_generations: &[&SearcherGeneration]) {
            self.gc_calls
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let active_segment_ids = live_generations
                .iter()
                .flat_map(|searcher_generation| searcher_generation.segments().keys().copied())
                .collect();
            *self.active_segment_ids.write().unwrap() = active_segment_ids;
        }
    }

    fn segment_ids(searcher: &Searcher) -> HashSet<SegmentId> {
        searcher
            .segment_readers()
            .iter()
            .map(|reader| reader.segment_id())
            .collect()
    }

    fn test_warming(num_warming_threads: usize) -> crate::Result<()> {
        let mut schema_builder = Schema::builder();
        let field = schema_builder.add_u64_field("pk", INDEXED);
        let schema = schema_builder.build();

        let directory = RamDirectory::create();
        let index = Index::create(directory, schema, IndexSettings::default())?;

        let num_writer_threads = 4;
        let mut writer = index
            .writer_with_num_threads(num_writer_threads, 25_000_000)
            .unwrap();

        for i in 0u64..1000u64 {
            writer.add_document(doc!(field => i))?;
        }
        writer.commit()?;

        let warmer1 = Arc::new(TestWarmer::default());
        let warmer2 = Arc::new(TestWarmer::default());
        warmer1.verify(0, 0, HashSet::new());
        warmer2.verify(0, 0, HashSet::new());

        let num_searchers = 4;
        let reader = index
            .reader_builder()
            .reload_policy(ReloadPolicy::Manual)
            .num_warming_threads(num_warming_threads)
            .num_searchers(num_searchers)
            .warmers(vec![
                Arc::downgrade(&warmer1) as Weak<dyn Warmer>,
                Arc::downgrade(&warmer2) as Weak<dyn Warmer>,
            ])
            .try_into()?;

        let warming_state = &reader.inner.warming_state;

        let searcher = reader.searcher();
        assert_eq!(searcher.segment_readers().len(), num_writer_threads);
        assert!(
            !warming_state.gc_maybe(),
            "no GC after first searcher generation"
        );
        warmer1.verify(1, 0, segment_ids(&searcher));
        warmer2.verify(1, 0, segment_ids(&searcher));
        assert_eq!(searcher.num_docs(), 1000);

        for i in 1000u64..2000u64 {
            writer.add_document(doc!(field => i))?;
        }
        writer.commit()?;
        writer.wait_merging_threads()?;

        drop(warmer1);

        let old_searcher = searcher;

        reader.reload()?;

        assert!(!warming_state.gc_maybe(), "old searcher still around");

        let searcher = reader.searcher();
        assert_eq!(searcher.num_docs(), 2000);

        warmer2.verify(
            2,
            0,
            segment_ids(&old_searcher)
                .union(&segment_ids(&searcher))
                .copied()
                .collect(),
        );

        drop(old_searcher);
        for _ in 0..num_searchers {
            // make sure the old searcher is dropped by the pool too
            let _ = reader.searcher();
        }
        assert!(warming_state.gc_maybe(), "old searcher dropped");

        warmer2.verify(2, 1, segment_ids(&searcher));

        Ok(())
    }

    #[test]
    fn warming_single_thread() -> crate::Result<()> {
        test_warming(1)
    }

    #[test]
    fn warming_four_threads() -> crate::Result<()> {
        test_warming(4)
    }
}
<commit_msg>Remove test assertion sensitive to background segment merging (#1274)<commit_after>use std::collections::HashSet;
use std::ops::Deref;
use std::sync::{Arc, Mutex, Weak};
use std::thread::JoinHandle;
use std::time::Duration;

use crate::{Executor, Inventory, Searcher, SearcherGeneration, TantivyError};

pub const GC_INTERVAL: Duration = Duration::from_secs(1);

/// `Warmer` can be used to maintain segment-level state e.g. caches.
///
/// They must be registered with the [IndexReaderBuilder].
pub trait Warmer: Sync + Send {
    /// Perform any warming work using the provided [Searcher].
    fn warm(&self, searcher: &Searcher) -> crate::Result<()>;

    /// Discards internal state for any [SearcherGeneration] not provided.
    fn garbage_collect(&self, live_generations: &[&SearcherGeneration]);
}

/// Warming-related state with interior mutability.
#[derive(Clone)]
pub(crate) struct WarmingState(Arc<Mutex<WarmingStateInner>>);

impl WarmingState {
    pub fn new(
        num_warming_threads: usize,
        warmers: Vec<Weak<dyn Warmer>>,
        searcher_generation_inventory: Inventory<SearcherGeneration>,
    ) -> crate::Result<Self> {
        Ok(Self(Arc::new(Mutex::new(WarmingStateInner {
            num_warming_threads,
            warmers,
            gc_thread: None,
            warmed_generation_ids: Default::default(),
            searcher_generation_inventory,
        }))))
    }

    /// Start tracking a new generation of [Searcher], and [Warmer::warm] it if there are active
    /// warmers.
    ///
    /// A background GC thread for [Warmer::garbage_collect] calls is uniquely created if there are
    /// active warmers.
    pub fn warm_new_searcher_generation(&self, searcher: &Searcher) -> crate::Result<()> {
        self.0
            .lock()
            .unwrap()
            .warm_new_searcher_generation(searcher, &self.0)
    }

    #[cfg(test)]
    fn gc_maybe(&self) -> bool {
        self.0.lock().unwrap().gc_maybe()
    }
}

struct WarmingStateInner {
    num_warming_threads: usize,
    warmers: Vec<Weak<dyn Warmer>>,
    gc_thread: Option<JoinHandle<()>>,
    // Contains all generations that have been warmed up.
    // This list is used to avoid triggers the individual Warmer GCs
    // if no warmed generation needs to be collected.
    warmed_generation_ids: HashSet<u64>,
    searcher_generation_inventory: Inventory<SearcherGeneration>,
}

impl WarmingStateInner {
    /// Start tracking provided searcher as an exemplar of a new generation.
    /// If there are active warmers, warm them with the provided searcher, and kick background GC
    /// thread if it has not yet been kicked. Otherwise, prune state for dropped searcher
    /// generations inline.
    fn warm_new_searcher_generation(
        &mut self,
        searcher: &Searcher,
        this: &Arc<Mutex<Self>>,
    ) -> crate::Result<()> {
        let warmers = self.pruned_warmers();
        // Avoid threads (warming as well as background GC) if there are no warmers
        if warmers.is_empty() {
            return Ok(());
        }
        self.start_gc_thread_maybe(this)?;
        self.warmed_generation_ids
            .insert(searcher.generation().generation_id());
        warming_executor(self.num_warming_threads.min(warmers.len()))?
            .map(|warmer| warmer.warm(searcher), warmers.into_iter())?;
        Ok(())
    }

    /// Attempt to upgrade the weak Warmer references, pruning those which cannot be upgraded.
    /// Return the strong references.
    fn pruned_warmers(&mut self) -> Vec<Arc<dyn Warmer>> {
        let strong_warmers = self
            .warmers
            .iter()
            .flat_map(|weak_warmer| weak_warmer.upgrade())
            .collect::<Vec<_>>();
        self.warmers = strong_warmers.iter().map(Arc::downgrade).collect();
        strong_warmers
    }

    /// [Warmer::garbage_collect] active warmers if some searcher generation is observed to have
    /// been dropped.
    fn gc_maybe(&mut self) -> bool {
        let live_generations = self.searcher_generation_inventory.list();
        let live_generation_ids: HashSet<u64> = live_generations
            .iter()
            .map(|searcher_generation| searcher_generation.generation_id())
            .collect();
        let gc_not_required = self
            .warmed_generation_ids
            .iter()
            .all(|warmed_up_generation| live_generation_ids.contains(warmed_up_generation));
        if gc_not_required {
            return false;
        }
        let live_generation_refs = live_generations
            .iter()
            .map(Deref::deref)
            .collect::<Vec<_>>();
        for warmer in self.pruned_warmers() {
            warmer.garbage_collect(&live_generation_refs);
        }
        self.warmed_generation_ids = live_generation_ids;
        true
    }

    /// Start GC thread if one has not already been started.
    fn start_gc_thread_maybe(&mut self, this: &Arc<Mutex<Self>>) -> crate::Result<bool> {
        if self.gc_thread.is_some() {
            return Ok(false);
        }
        let weak_inner = Arc::downgrade(this);
        let handle = std::thread::Builder::new()
            .name("tantivy-warm-gc".to_owned())
            .spawn(|| Self::gc_loop(weak_inner))
            .map_err(|_| {
                TantivyError::SystemError("Failed to spawn warmer GC thread".to_owned())
            })?;
        self.gc_thread = Some(handle);
        Ok(true)
    }

    /// Every [GC_INTERVAL] attempt to GC, with panics caught and logged using
    /// [std::panic::catch_unwind].
    fn gc_loop(inner: Weak<Mutex<WarmingStateInner>>) {
        for _ in crossbeam::channel::tick(GC_INTERVAL) {
            if let Some(inner) = inner.upgrade() {
                // rely on deterministic gc in tests
                #[cfg(not(test))]
                if let Err(err) = std::panic::catch_unwind(|| inner.lock().unwrap().gc_maybe()) {
                    error!("Panic in Warmer GC {:?}", err);
                }
                // avoid unused var warning in tests
                #[cfg(test)]
                drop(inner);
            }
        }
    }
}

fn warming_executor(num_threads: usize) -> crate::Result<Executor> {
    if num_threads <= 1 {
        Ok(Executor::single_thread())
    } else {
        Executor::multi_thread(num_threads, "tantivy-warm-")
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashSet;
    use std::sync::atomic::{self, AtomicUsize};
    use std::sync::{Arc, RwLock, Weak};

    use super::Warmer;
    use crate::core::searcher::SearcherGeneration;
    use crate::directory::RamDirectory;
    use crate::schema::{Schema, INDEXED};
    use crate::{Index, IndexSettings, ReloadPolicy, Searcher, SegmentId};

    #[derive(Default)]
    struct TestWarmer {
        active_segment_ids: RwLock<HashSet<SegmentId>>,
        warm_calls: AtomicUsize,
        gc_calls: AtomicUsize,
    }

    impl TestWarmer {
        fn live_segment_ids(&self) -> HashSet<SegmentId> {
            self.active_segment_ids.read().unwrap().clone()
        }

        fn warm_calls(&self) -> usize {
            self.warm_calls.load(atomic::Ordering::Acquire)
        }

        fn gc_calls(&self) -> usize {
            self.gc_calls.load(atomic::Ordering::Acquire)
        }

        fn verify(
            &self,
            expected_warm_calls: usize,
            expected_gc_calls: usize,
            expected_segment_ids: HashSet<SegmentId>,
        ) {
            assert_eq!(self.warm_calls(), expected_warm_calls);
            assert_eq!(self.gc_calls(), expected_gc_calls);
            assert_eq!(self.live_segment_ids(), expected_segment_ids);
        }
    }

    impl Warmer for TestWarmer {
        fn warm(&self, searcher: &crate::Searcher) -> crate::Result<()> {
            self.warm_calls.fetch_add(1, atomic::Ordering::SeqCst);
            for reader in searcher.segment_readers() {
                self.active_segment_ids
                    .write()
                    .unwrap()
                    .insert(reader.segment_id());
            }
            Ok(())
        }

        fn garbage_collect(&self, live_generations: &[&SearcherGeneration]) {
            self.gc_calls
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let active_segment_ids = live_generations
                .iter()
                .flat_map(|searcher_generation| searcher_generation.segments().keys().copied())
                .collect();
            *self.active_segment_ids.write().unwrap() = active_segment_ids;
        }
    }

    fn segment_ids(searcher: &Searcher) -> HashSet<SegmentId> {
        searcher
            .segment_readers()
            .iter()
            .map(|reader| reader.segment_id())
            .collect()
    }

    fn test_warming(num_warming_threads: usize) -> crate::Result<()> {
        let mut schema_builder = Schema::builder();
        let field = schema_builder.add_u64_field("pk", INDEXED);
        let schema = schema_builder.build();

        let directory = RamDirectory::create();
        let index = Index::create(directory, schema, IndexSettings::default())?;

        let num_writer_threads = 4;
        let mut writer = index
            .writer_with_num_threads(num_writer_threads, 25_000_000)
            .unwrap();

        for i in 0u64..1000u64 {
            writer.add_document(doc!(field => i))?;
        }
        writer.commit()?;

        let warmer1 = Arc::new(TestWarmer::default());
        let warmer2 = Arc::new(TestWarmer::default());
        warmer1.verify(0, 0, HashSet::new());
        warmer2.verify(0, 0, HashSet::new());

        let num_searchers = 4;
        let reader = index
            .reader_builder()
            .reload_policy(ReloadPolicy::Manual)
            .num_warming_threads(num_warming_threads)
            .num_searchers(num_searchers)
            .warmers(vec![
                Arc::downgrade(&warmer1) as Weak<dyn Warmer>,
                Arc::downgrade(&warmer2) as Weak<dyn Warmer>,
            ])
            .try_into()?;

        let warming_state = &reader.inner.warming_state;

        let searcher = reader.searcher();
        assert!(
            !warming_state.gc_maybe(),
            "no GC after first searcher generation"
        );
        warmer1.verify(1, 0, segment_ids(&searcher));
        warmer2.verify(1, 0, segment_ids(&searcher));
        assert_eq!(searcher.num_docs(), 1000);

        for i in 1000u64..2000u64 {
            writer.add_document(doc!(field => i))?;
        }
        writer.commit()?;
        writer.wait_merging_threads()?;

        drop(warmer1);

        let old_searcher = searcher;

        reader.reload()?;

        assert!(!warming_state.gc_maybe(), "old searcher still around");

        let searcher = reader.searcher();
        assert_eq!(searcher.num_docs(), 2000);

        warmer2.verify(
            2,
            0,
            segment_ids(&old_searcher)
                .union(&segment_ids(&searcher))
                .copied()
                .collect(),
        );

        drop(old_searcher);
        for _ in 0..num_searchers {
            // make sure the old searcher is dropped by the pool too
            let _ = reader.searcher();
        }
        assert!(warming_state.gc_maybe(), "old searcher dropped");

        warmer2.verify(2, 1, segment_ids(&searcher));

        Ok(())
    }

    #[test]
    fn warming_single_thread() -> crate::Result<()> {
        test_warming(1)
    }

    #[test]
    fn warming_four_threads() -> crate::Result<()> {
        test_warming(4)
    }
}
<|endoftext|>


<commit_before>//! Gamepad utility functions.
//!
//! This is going to be a bit of a work-in-progress as gamepad input
//! gets fleshed out.  The `gilrs` crate needs help to add better
//! cross-platform support.  Why not give it a hand?
use gilrs::ConnectedGamepadsIterator;
use std::fmt;

pub use gilrs::{self, Event, Gamepad, Gilrs};

/// A unique identifier for a particular GamePad
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GamepadId(pub(crate) gilrs::GamepadId);

use crate::context::Context;
use crate::error::GameResult;

/// Trait object defining a gamepad/joystick context.
pub trait GamepadContext {
    /// Returns a gamepad event.
    fn next_event(&mut self) -> Option<Event>;

    /// returns the `Gamepad` associated with an id.
    fn gamepad(&self, id: GamepadId) -> Gamepad;

    /// returns an iterator over the connected `Gamepad`s.
    fn gamepads(&self) -> GamepadsIterator;
}

/// A structure that contains gamepad state using `gilrs`.
pub struct GilrsGamepadContext {
    pub(crate) gilrs: Gilrs,
}

impl fmt::Debug for GilrsGamepadContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<GilrsGamepadContext: {:p}>", self)
    }
}

impl GilrsGamepadContext {
    pub(crate) fn new() -> GameResult<Self> {
        let gilrs = Gilrs::new()?;
        Ok(GilrsGamepadContext { gilrs })
    }
}

impl GamepadContext for GilrsGamepadContext {
    fn next_event(&mut self) -> Option<Event> {
        self.gilrs.next_event()
    }

    fn gamepad(&self, id: GamepadId) -> Gamepad {
        self.gilrs.gamepad(id.0)
    }

    fn gamepads(&self) -> GamepadsIterator {
        GamepadsIterator {
            wrapped: self.gilrs.gamepads(),
        }
    }
}

/// An iterator of the connected gamepads
pub struct GamepadsIterator<'a> {
    wrapped: ConnectedGamepadsIterator<'a>,
}

impl<'a> fmt::Debug for GamepadsIterator<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<GamepadsIterator: {:p}>", self)
    }
}

impl<'a> Iterator for GamepadsIterator<'a> {
    type Item = (GamepadId, Gamepad<'a>);

    fn next(&mut self) -> Option<(GamepadId, Gamepad<'a>)> {
        match self.wrapped.next() {
            Some((id, gp)) => Some((GamepadId(id), gp)),
            None => None,
        }
    }
}

/// A structure that implements [`GamepadContext`](trait.GamepadContext.html)
/// but does nothing; a stub for when you don't need it or are
/// on a platform that `gilrs` doesn't support.
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct NullGamepadContext {}

impl GamepadContext for NullGamepadContext {
    fn next_event(&mut self) -> Option<Event> {
        panic!("Gamepad module disabled")
    }

    fn gamepad(&self, _id: GamepadId) -> Gamepad {
        panic!("Gamepad module disabled")
    }

    fn gamepads(&self) -> GamepadsIterator {
        panic!("Gamepad module disabled")
    }
}

/// Returns the `Gamepad` associated with an `id`.
pub fn gamepad(ctx: &Context, id: GamepadId) -> Gamepad {
    ctx.gamepad_context.gamepad(id)
}

/// Return an iterator of all the `Gamepads` that are connected.
pub fn gamepads(ctx: &Context) -> GamepadsIterator {
    ctx.gamepad_context.gamepads()
}

// Properties gamepads might want:
// Number of buttons
// Number of axes
// Name/ID
// Is it connected?  (For consoles?)
// Whether or not they support vibration

/*
/// Lists all gamepads.  With metainfo, maybe?
pub fn list_gamepads() {
    unimplemented!()
}

/// Returns the state of the given axis on a gamepad.
pub fn axis() {
    unimplemented!()
}

/// Returns the state of the given button on a gamepad.
pub fn button_pressed() {
    unimplemented!()
}
*/

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

    #[test]
    fn gilrs_init() {
        assert!(GilrsGamepadContext::new().is_ok());
    }
}
<commit_msg>added GilrsGamepadContext::from() method<commit_after>//! Gamepad utility functions.
//!
//! This is going to be a bit of a work-in-progress as gamepad input
//! gets fleshed out.  The `gilrs` crate needs help to add better
//! cross-platform support.  Why not give it a hand?
use gilrs::ConnectedGamepadsIterator;
use std::fmt;

pub use gilrs::{self, Event, Gamepad, Gilrs};

/// A unique identifier for a particular GamePad
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GamepadId(pub(crate) gilrs::GamepadId);

use crate::context::Context;
use crate::error::GameResult;

/// Trait object defining a gamepad/joystick context.
pub trait GamepadContext {
    /// Returns a gamepad event.
    fn next_event(&mut self) -> Option<Event>;

    /// returns the `Gamepad` associated with an id.
    fn gamepad(&self, id: GamepadId) -> Gamepad;

    /// returns an iterator over the connected `Gamepad`s.
    fn gamepads(&self) -> GamepadsIterator;
}

/// A structure that contains gamepad state using `gilrs`.
pub struct GilrsGamepadContext {
    pub(crate) gilrs: Gilrs,
}

impl fmt::Debug for GilrsGamepadContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<GilrsGamepadContext: {:p}>", self)
    }
}

impl GilrsGamepadContext {
    pub(crate) fn new() -> GameResult<Self> {
        let gilrs = Gilrs::new()?;
        Ok(GilrsGamepadContext { gilrs })
    }

    /// Returns a `GilrsGamepadContext` with the entered Gilrs
    pub fn from(custom_gilrs: Gilrs) -> Self {
        GilrsGamepadContext {
            gilrs: custom_gilrs,
        }
    }
}

impl GamepadContext for GilrsGamepadContext {
    fn next_event(&mut self) -> Option<Event> {
        self.gilrs.next_event()
    }

    fn gamepad(&self, id: GamepadId) -> Gamepad {
        self.gilrs.gamepad(id.0)
    }

    fn gamepads(&self) -> GamepadsIterator {
        GamepadsIterator {
            wrapped: self.gilrs.gamepads(),
        }
    }
}

/// An iterator of the connected gamepads
pub struct GamepadsIterator<'a> {
    wrapped: ConnectedGamepadsIterator<'a>,
}

impl<'a> fmt::Debug for GamepadsIterator<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<GamepadsIterator: {:p}>", self)
    }
}

impl<'a> Iterator for GamepadsIterator<'a> {
    type Item = (GamepadId, Gamepad<'a>);

    fn next(&mut self) -> Option<(GamepadId, Gamepad<'a>)> {
        match self.wrapped.next() {
            Some((id, gp)) => Some((GamepadId(id), gp)),
            None => None,
        }
    }
}

/// A structure that implements [`GamepadContext`](trait.GamepadContext.html)
/// but does nothing; a stub for when you don't need it or are
/// on a platform that `gilrs` doesn't support.
#[derive(Debug, Clone, Copy, Default)]
pub(crate) struct NullGamepadContext {}

impl GamepadContext for NullGamepadContext {
    fn next_event(&mut self) -> Option<Event> {
        panic!("Gamepad module disabled")
    }

    fn gamepad(&self, _id: GamepadId) -> Gamepad {
        panic!("Gamepad module disabled")
    }

    fn gamepads(&self) -> GamepadsIterator {
        panic!("Gamepad module disabled")
    }
}

/// Returns the `Gamepad` associated with an `id`.
pub fn gamepad(ctx: &Context, id: GamepadId) -> Gamepad {
    ctx.gamepad_context.gamepad(id)
}

/// Return an iterator of all the `Gamepads` that are connected.
pub fn gamepads(ctx: &Context) -> GamepadsIterator {
    ctx.gamepad_context.gamepads()
}

// Properties gamepads might want:
// Number of buttons
// Number of axes
// Name/ID
// Is it connected?  (For consoles?)
// Whether or not they support vibration

/*
/// Lists all gamepads.  With metainfo, maybe?
pub fn list_gamepads() {
    unimplemented!()
}

/// Returns the state of the given axis on a gamepad.
pub fn axis() {
    unimplemented!()
}

/// Returns the state of the given button on a gamepad.
pub fn button_pressed() {
    unimplemented!()
}
*/

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

    #[test]
    fn gilrs_init() {
        assert!(GilrsGamepadContext::new().is_ok());
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use std::fmt;
use std::collections::{HashMap, BTreeMap};
use visit::{Visitor};
use ptr::{B};
use std::cell::RefCell;

struct ExpressionEvaluator;

/*
impl SymbolVisitor for ExpressionEvaluator{
	pub fn visit_num(&self, num_exp : &NumExpression){

	}

	pub fn visit_ident(&self, num_exp : &IdentExpr){

	}
}
*/
pub type OptionalExprList = Option<Vec<B<Expr>>>;
pub type OptionalExpr = Option<B<Expr>>;
pub type OptionalTypeExprTupleList = Option<Vec<(TType, B<Expr>)>>;
pub type OptionalParamInfoList = Option<Vec<(String, TType)>>;

#[derive(Debug, PartialEq, Clone)]
pub enum TType{
    TInt32,
    TString,
    TArray(B<TType>), //TType can be anything
    TRecord,
    TCustom(String),
    TNil,
    TVoid
}

///FIXME remove this
pub enum TypeValue{
    TInt32(i32),
    TString(String),
    TIdent(String)
}

impl fmt::Display for TType{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
        match *self{
            TType::TInt32 => f.write_str("Number"),
            TType::TString => f.write_str("String"),
            TType::TArray(ref T) => f.write_str("Array of some type"),
            TType::TRecord => f.write_str("Record"),
            TType::TCustom(ref name) => f.write_str("Custom"),
            TType::TNil => f.write_str("Nil"),
            TType::TVoid => f.write_str("Void")
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Binding{
    TypeBinding(TType),
    VarBinding(TType),
    FuncBinding(TType)
}

pub trait Statement{
    fn generate_code(&self)->Vec<String>;
}

pub struct Block{
    pub sym_tab : RefCell<HashMap<String, TType>>,
    pub statements : Vec<B<Stmt>>, //trait is boxed because it has no size known at compile-time. this is a trait object.
    pub instructions : Vec<String>,
    pub expr : Option<B<Expr>>, //this holds the main expr as in the production program -> expr
    // pub child_block : Option<Block>,
    // pub parent_block : Option<Block>
}

impl Block{
    pub fn new()->Self{
        Block {sym_tab : RefCell::new(HashMap::new()),
               statements : Vec::new(),
               instructions : Vec::new(),
               expr : None,
            //    child_block : None,
            //    parent_block : None

        }
    }

    pub fn add_sym(&mut self, sym_id : String, ty : TType){
        self.sym_tab.borrow_mut().insert(sym_id, ty);
    }

    pub fn contains(&self, sym_id : &String)->bool{
        //FIXME use contains key
        match self.sym_tab.borrow().get(sym_id){
            Some(s) => true,
            _ => false
        }
        //false
    }

    pub fn generate(&mut self){
        for s in &self.statements{
            /*for i in &s.generate_code(){
                println!("{}", i);
            }*/
            //self.instructions.extend(s.generate_code().into_iter());
        }
    }
}

pub enum Expr{
   //let dec+ in exp*; end
   //note: instead of making a list of exprs as the grammar suggests,
   //use a seq-expr. This will make parsing easier.
   //i.e. we don't want this:
   //let var v := 6
   //in print(v);
   //   print("a");
   //   print("b")
   //end
   //instead we want this (note the parens):
   //let var v:= 6
   //in (print(v);
   //    print("a");
   //    print("b")
   //   )
   //end
   LetExpr(Vec<Decl>, OptionalExpr),
   //id
   IdExpr(String),
   //nil
   NilExpr,
   //FIXME is this needed?
   LitExpr,
   //stringLit
   StringExpr(String),
   //break
   BreakExpr,
   //id ( exp*, )
   CallExpr(String, OptionalTypeExprTupleList),
   //intLit
   NumExpr(i32),
   //( exp*; )
   SeqExpr(OptionalExprList),

   AddExpr(B<Expr>, B<Expr>),
   SubExpr(B<Expr>, B<Expr>),
   MulExpr(B<Expr>, B<Expr>),
   DivExpr(B<Expr>, B<Expr>),
   ModExpr(B<Expr>, B<Expr>),
   BlockExpr(B<Block>),
   IfThenExpr(B<Expr>, B<Expr>),
   IfThenElseExpr(B<Expr>, B<Expr>, B<Expr>),
   WhileExpr(B<Expr>, B<Expr>),
   ForExpr(String, B<Expr>, B<Expr>, B<Expr>),
   AssignExpr(String, B<Expr>),
   LabelExpr(String),
   GotoExpr(String)
}

pub struct FieldDec{
    id : String,
    ty : TType
}

//lst.where(move |x|{x.id == "id"}).first()

pub enum Decl{
    //type tyId = ty
    TypeDec(String, TType),
    //var a : int := 1
    VarDec(String, TType, B<Expr>),
    //function id ( fieldDec; ) : tyId = exp
    FunDec(String, OptionalParamInfoList, TType, B<Expr>)
}

//FIXME remove this
pub struct Local{
    pub ident : String,
    pub ty : TType,
    pub expr : B<Expr>
}

impl Local{
    pub fn new(ident : String, ty : TType, expr : B<Expr>) -> Local{
        Local {ident : ident, ty : ty, expr : expr}
    }
}

//FIXME remove this
pub enum Stmt{
     VarDeclStmt(Local),
     ExprStmt(B<Expr>)
}
<commit_msg>Add expressions for less-than, greater-than<commit_after>#![allow(dead_code)]

use std::fmt;
use std::collections::{HashMap, BTreeMap};
use visit::{Visitor};
use ptr::{B};
use std::cell::RefCell;

struct ExpressionEvaluator;

/*
impl SymbolVisitor for ExpressionEvaluator{
	pub fn visit_num(&self, num_exp : &NumExpression){

	}

	pub fn visit_ident(&self, num_exp : &IdentExpr){

	}
}
*/
pub type OptionalExprList = Option<Vec<B<Expr>>>;
pub type OptionalExpr = Option<B<Expr>>;
pub type OptionalTypeExprTupleList = Option<Vec<(TType, B<Expr>)>>;
pub type OptionalParamInfoList = Option<Vec<(String, TType)>>;

#[derive(Debug, PartialEq, Clone)]
pub enum TType{
    TInt32,
    TString,
    TArray(B<TType>), //TType can be anything
    TRecord,
    TCustom(String),
    TNil,
    TVoid
}

///FIXME remove this
pub enum TypeValue{
    TInt32(i32),
    TString(String),
    TIdent(String)
}

impl fmt::Display for TType{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result{
        match *self{
            TType::TInt32 => f.write_str("Number"),
            TType::TString => f.write_str("String"),
            TType::TArray(ref T) => f.write_str("Array of some type"),
            TType::TRecord => f.write_str("Record"),
            TType::TCustom(ref name) => f.write_str("Custom"),
            TType::TNil => f.write_str("Nil"),
            TType::TVoid => f.write_str("Void")
        }
    }
}

#[derive(Debug, PartialEq, Clone)]
pub enum Binding{
    TypeBinding(TType),
    VarBinding(TType),
    FuncBinding(TType)
}

pub trait Statement{
    fn generate_code(&self)->Vec<String>;
}

pub struct Block{
    pub sym_tab : RefCell<HashMap<String, TType>>,
    pub statements : Vec<B<Stmt>>, //trait is boxed because it has no size known at compile-time. this is a trait object.
    pub instructions : Vec<String>,
    pub expr : Option<B<Expr>>, //this holds the main expr as in the production program -> expr
    // pub child_block : Option<Block>,
    // pub parent_block : Option<Block>
}

impl Block{
    pub fn new()->Self{
        Block {sym_tab : RefCell::new(HashMap::new()),
               statements : Vec::new(),
               instructions : Vec::new(),
               expr : None,
            //    child_block : None,
            //    parent_block : None

        }
    }

    pub fn add_sym(&mut self, sym_id : String, ty : TType){
        self.sym_tab.borrow_mut().insert(sym_id, ty);
    }

    pub fn contains(&self, sym_id : &String)->bool{
        //FIXME use contains key
        match self.sym_tab.borrow().get(sym_id){
            Some(s) => true,
            _ => false
        }
        //false
    }

    pub fn generate(&mut self){
        for s in &self.statements{
            /*for i in &s.generate_code(){
                println!("{}", i);
            }*/
            //self.instructions.extend(s.generate_code().into_iter());
        }
    }
}

pub enum Expr{
   //let dec+ in exp*; end
   //note: instead of making a list of exprs as the grammar suggests,
   //use a seq-expr. This will make parsing easier.
   //i.e. we don't want this:
   //let var v := 6
   //in print(v);
   //   print("a");
   //   print("b")
   //end
   //instead we want this (note the parens):
   //let var v:= 6
   //in (print(v);
   //    print("a");
   //    print("b")
   //   )
   //end
   LetExpr(Vec<Decl>, OptionalExpr),
   //id
   IdExpr(String),
   //nil
   NilExpr,
   //FIXME is this needed?
   LitExpr,
   //stringLit
   StringExpr(String),
   //break
   BreakExpr,
   //id ( exp*, )
   CallExpr(String, OptionalTypeExprTupleList),
   //intLit
   NumExpr(i32),
   //( exp*; )
   SeqExpr(OptionalExprList),

   AddExpr(B<Expr>, B<Expr>),
   SubExpr(B<Expr>, B<Expr>),
   MulExpr(B<Expr>, B<Expr>),
   DivExpr(B<Expr>, B<Expr>),
   LessThanExpr(B<Expr>, B<Expr>),
   GreaterThanExpr(B<Expr>, B<Expr>),
   ModExpr(B<Expr>, B<Expr>),
   BlockExpr(B<Block>),
   IfThenExpr(B<Expr>, B<Expr>),
   IfThenElseExpr(B<Expr>, B<Expr>, B<Expr>),
   WhileExpr(B<Expr>, B<Expr>),
   ForExpr(String, B<Expr>, B<Expr>, B<Expr>),
   AssignExpr(String, B<Expr>),
   LabelExpr(String),
   GotoExpr(String)
}

pub struct FieldDec{
    id : String,
    ty : TType
}

//lst.where(move |x|{x.id == "id"}).first()

pub enum Decl{
    //type tyId = ty
    TypeDec(String, TType),
    //var a : int := 1
    VarDec(String, TType, B<Expr>),
    //function id ( fieldDec; ) : tyId = exp
    FunDec(String, OptionalParamInfoList, TType, B<Expr>)
}

//FIXME remove this
pub struct Local{
    pub ident : String,
    pub ty : TType,
    pub expr : B<Expr>
}

impl Local{
    pub fn new(ident : String, ty : TType, expr : B<Expr>) -> Local{
        Local {ident : ident, ty : ty, expr : expr}
    }
}

//FIXME remove this
pub enum Stmt{
     VarDeclStmt(Local),
     ExprStmt(B<Expr>)
}
<|endoftext|>


<commit_before>// Purge all references to a crate's version from the database.
//
// Please be super sure you want to do this before running this.
//
// Usage:
//      cargo run --bin delete-version crate-name version-number

#![warn(clippy::all, rust_2018_idioms)]

use cargo_registry::{
    db,
    models::{Crate, Version},
    schema::versions,
};
use std::{
    env,
    io::{self, prelude::*},
};

use diesel::prelude::*;

fn main() {
    let conn = db::connect_now().unwrap();
    conn.transaction::<_, diesel::result::Error, _>(|| {
        delete(&conn);
        Ok(())
    })
    .unwrap()
}

fn delete(conn: &PgConnection) {
    let name = match env::args().nth(1) {
        None => {
            println!("needs a crate-name argument");
            return;
        }
        Some(s) => s,
    };
    let version = match env::args().nth(2) {
        None => {
            println!("needs a version argument");
            return;
        }
        Some(s) => s,
    };

    let krate: Crate = Crate::by_name(&name).first(conn).unwrap();
    let v: Version = Version::belonging_to(&krate)
        .filter(versions::num.eq(&version))
        .first(conn)
        .unwrap();
    print!(
        "Are you sure you want to delete {}#{} ({}) [y/N]: ",
        name, version, v.id
    );
    io::stdout().flush().unwrap();
    let mut line = String::new();
    io::stdin().read_line(&mut line).unwrap();
    if !line.starts_with('y') {
        return;
    }

    println!("deleting version {} ({})", v.num, v.id);
    diesel::delete(versions::table.find(&v.id))
        .execute(conn)
        .unwrap();

    print!("commit? [y/N]: ");
    io::stdout().flush().unwrap();
    let mut line = String::new();
    io::stdin().read_line(&mut line).unwrap();
    if !line.starts_with('y') {
        panic!("aborting transaction");
    }
}
<commit_msg>bin/delete-version: Use `clap` for CLI argument parsing<commit_after>#![warn(clippy::all, rust_2018_idioms)]

use cargo_registry::{
    db,
    models::{Crate, Version},
    schema::versions,
};
use std::io::{self, prelude::*};

use clap::Clap;
use diesel::prelude::*;

#[derive(Clap, Debug)]
#[clap(
    name = "delete-version",
    about = "Purge all references to a crate's version from the database.\n\nPlease be super sure you want to do this before running this."
)]
struct Opts {
    /// Name of the crate
    crate_name: String,
    /// Version number that should be deleted
    version: String,
}

fn main() {
    let conn = db::connect_now().unwrap();
    conn.transaction::<_, diesel::result::Error, _>(|| {
        delete(&conn);
        Ok(())
    })
    .unwrap()
}

fn delete(conn: &PgConnection) {
    let opts: Opts = Opts::parse();

    let krate: Crate = Crate::by_name(&opts.crate_name).first(conn).unwrap();
    let v: Version = Version::belonging_to(&krate)
        .filter(versions::num.eq(&opts.version))
        .first(conn)
        .unwrap();
    print!(
        "Are you sure you want to delete {}#{} ({}) [y/N]: ",
        opts.crate_name, opts.version, v.id
    );
    io::stdout().flush().unwrap();
    let mut line = String::new();
    io::stdin().read_line(&mut line).unwrap();
    if !line.starts_with('y') {
        return;
    }

    println!("deleting version {} ({})", v.num, v.id);
    diesel::delete(versions::table.find(&v.id))
        .execute(conn)
        .unwrap();

    print!("commit? [y/N]: ");
    io::stdout().flush().unwrap();
    let mut line = String::new();
    io::stdin().read_line(&mut line).unwrap();
    if !line.starts_with('y') {
        panic!("aborting transaction");
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate getopts;
extern crate rocket;
extern crate serde_json;
#[macro_use] extern crate rocket_contrib;

extern crate pombase;

use std::sync::Mutex;
use std::process;
use std::env;

use getopts::Options;

use rocket_contrib::{Json, Value};

use pombase::api::query::Query;
use pombase::api::result::Result;
use pombase::api::query_exec::QueryExec;
use pombase::api::server_data::ServerData;

const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

#[post("/query", data="<q>", format = "application/json")]
fn query_post(q: Json<Query>, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<Result>> {
    let query_exec = state.lock().expect("failed to lock");
    Some(Json(query_exec.exec(&q.into_inner())))
}

#[get ("/reload")]
fn reload(state: rocket::State<Mutex<QueryExec>>) {
    let mut query_exec = state.lock().expect("failed to lock");
    print!("reloading ...\n");
    query_exec.reload();
}

#[get ("/ping")]
fn ping() -> Option<String> {
    Some(String::from("OK") + " " + PKG_NAME + " " + VERSION)
}

#[error(404)]
fn not_found() -> Json<Value> {
    Json(json!({
        "status": "error",
        "reason": "Resource was not found."
    }))
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    print!("{} v{}\n", PKG_NAME, VERSION);

    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "print this help message");
    opts.optopt("c", "config-file", "Configuration file name", "CONFIG");
    opts.optopt("m", "search-maps", "Search data", "MAPS_JSON_FILE");
    opts.optopt("s", "gene-subsets", "Gene subset data", "SUBSETS_JSON_FILE");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let program = args[0].clone();

    if matches.opt_present("help") {
        print_usage(&program, opts);
        process::exit(0);
    }

    if !matches.opt_present("config-file") {
        print!("no -c|--config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("search-maps") {
        print!("no --search-maps|-m option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("gene-subsets") {
        print!("no --gene-subsets|-s option\n");
        print_usage(&program, opts);
        process::exit(1);
    }

    let search_maps_filename = matches.opt_str("m").unwrap();
    let gene_subsets_filename = matches.opt_str("s").unwrap();
    println!("Reading data files ...");

    let config_file_name = matches.opt_str("c").unwrap();
    let server_data = ServerData::new(&config_file_name, &search_maps_filename,
                                      &gene_subsets_filename);
    let query_exec = QueryExec::new(server_data);

    println!("Starting server ...");
    rocket::ignite()
        .mount("/", routes![query_post, reload, ping])
        .catch(errors![not_found])
        .manage(Mutex::new(query_exec))
        .launch();
}
<commit_msg>Print a message when reloading finishes<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate getopts;
extern crate rocket;
extern crate serde_json;
#[macro_use] extern crate rocket_contrib;

extern crate pombase;

use std::sync::Mutex;
use std::process;
use std::env;

use getopts::Options;

use rocket_contrib::{Json, Value};

use pombase::api::query::Query;
use pombase::api::result::Result;
use pombase::api::query_exec::QueryExec;
use pombase::api::server_data::ServerData;

const PKG_NAME: &str = env!("CARGO_PKG_NAME");
const VERSION: &str = env!("CARGO_PKG_VERSION");

#[post("/query", data="<q>", format = "application/json")]
fn query_post(q: Json<Query>, state: rocket::State<Mutex<QueryExec>>) -> Option<Json<Result>> {
    let query_exec = state.lock().expect("failed to lock");
    Some(Json(query_exec.exec(&q.into_inner())))
}

#[get ("/reload")]
fn reload(state: rocket::State<Mutex<QueryExec>>) {
    let mut query_exec = state.lock().expect("failed to lock");
    print!("reloading ...\n");
    query_exec.reload();
    print!("... done\n");
}

#[get ("/ping")]
fn ping() -> Option<String> {
    Some(String::from("OK") + " " + PKG_NAME + " " + VERSION)
}

#[error(404)]
fn not_found() -> Json<Value> {
    Json(json!({
        "status": "error",
        "reason": "Resource was not found."
    }))
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    print!("{} v{}\n", PKG_NAME, VERSION);

    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();

    opts.optflag("h", "help", "print this help message");
    opts.optopt("c", "config-file", "Configuration file name", "CONFIG");
    opts.optopt("m", "search-maps", "Search data", "MAPS_JSON_FILE");
    opts.optopt("s", "gene-subsets", "Gene subset data", "SUBSETS_JSON_FILE");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    let program = args[0].clone();

    if matches.opt_present("help") {
        print_usage(&program, opts);
        process::exit(0);
    }

    if !matches.opt_present("config-file") {
        print!("no -c|--config-file option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("search-maps") {
        print!("no --search-maps|-m option\n");
        print_usage(&program, opts);
        process::exit(1);
    }
    if !matches.opt_present("gene-subsets") {
        print!("no --gene-subsets|-s option\n");
        print_usage(&program, opts);
        process::exit(1);
    }

    let search_maps_filename = matches.opt_str("m").unwrap();
    let gene_subsets_filename = matches.opt_str("s").unwrap();
    println!("Reading data files ...");

    let config_file_name = matches.opt_str("c").unwrap();
    let server_data = ServerData::new(&config_file_name, &search_maps_filename,
                                      &gene_subsets_filename);
    let query_exec = QueryExec::new(server_data);

    println!("Starting server ...");
    rocket::ignite()
        .mount("/", routes![query_post, reload, ping])
        .catch(errors![not_found])
        .manage(Mutex::new(query_exec))
        .launch();
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_char, free};
use std::ffi::CString;
use std::os::raw::c_void;
use std::slice;

mod open_spiel_bindings;
use open_spiel_bindings::*;

fn convert_and_free_cstring(c_buf: *mut c_char, len: u64) -> String {
    let bytes = unsafe { std::slice::from_raw_parts(c_buf as *const u8, len as usize) };
    let str_slice = unsafe { std::str::from_utf8_unchecked(bytes) };
    let str_buf: String = str_slice.to_owned();
    unsafe { free(c_buf as *mut c_void) };
    str_buf
}

pub struct GameParameters {
    params: *mut c_void,
}

pub struct State {
    state: *mut c_void,
}

pub struct Game {
    game: *mut c_void,
}

pub struct Bot {
    bot: *mut c_void,
}

impl Default for GameParameters {
    fn default() -> Self {
        Self { params: unsafe { NewGameParameters() } }
    }
}

impl GameParameters {
    pub fn set_int(&mut self, key: &str, value: i32) {
        let key = CString::new(key).unwrap();
        unsafe {
            GameParametersSetInt(self.params, key.as_ptr(), value);
        }
    }

    pub fn set_f64(&mut self, key: &str, value: f64) {
        let key = CString::new(key).unwrap();
        unsafe {
            GameParametersSetDouble(self.params, key.as_ptr(), value);
        }
    }

    pub fn set_str(&mut self, key: &str, value: &str) {
        let key = CString::new(key).unwrap();
        let value = CString::new(value).unwrap();
        unsafe {
            GameParametersSetString(self.params, key.as_ptr(), value.as_ptr());
        }
    }
}

impl Drop for GameParameters {
    fn drop(&mut self) {
        unsafe { DeleteGameParameters(self.params) }
    }
}

unsafe impl Send for GameParameters {}
unsafe impl Sync for GameParameters {}

impl State {
    pub fn new(sptr: *mut c_void) -> State {
        State { state: sptr }
    }

    pub fn current_player(&self) -> i32 {
        unsafe { StateCurrentPlayer(self.state) }
    }

    pub fn clone(&self) -> State {
        unsafe { State { state: StateClone(self.state) } }
    }

    pub fn is_chance_node(&self) -> bool {
        let ret = unsafe { StateIsChanceNode(self.state) };
        ret == 1
    }

    pub fn is_terminal(&self) -> bool {
        let ret = unsafe { StateIsTerminal(self.state) };
        ret == 1
    }

    pub fn num_players(&self) -> usize {
        unsafe { StateNumPlayers(self.state) as usize }
    }

    pub fn returns(&self) -> Vec<f64> {
        let length = self.num_players();
        let mut returns_vec = Vec::with_capacity(length);
        unsafe {
            StateReturns(self.state, returns_vec.as_mut_ptr());
            returns_vec.set_len(length);
        }
        returns_vec
    }

    pub fn player_return(&self, player: i32) -> f64 {
        unsafe { StatePlayerReturn(self.state, player) }
    }

    pub fn legal_actions(&self) -> Vec<i64> {
        let mut c_num_legal_moves = 0;
        let c_buf = unsafe { StateLegalActions(self.state, &mut c_num_legal_moves) };
        unsafe {
            let vec = slice::from_raw_parts(c_buf, c_num_legal_moves as usize).to_vec();
            free(c_buf as *mut c_void);
            vec
        }
    }

    pub fn chance_outcomes(&self) -> Vec<(i64, f64)> {
        let legal_actions: Vec<i64> = self.legal_actions();
        let mut size = 0;
        let c_buf = unsafe { StateChanceOutcomeProbs(self.state, &mut size) };
        let length = size as usize;
        let mut vec = vec![(0, 0.0); length];
        unsafe {
            let probs_slice = slice::from_raw_parts(c_buf, length);
            for i in 0..length {
                vec[i] = (legal_actions[i], probs_slice[i]);
            }
            free(c_buf as *mut c_void);
        }
        vec
    }

    pub fn apply_action(&self, action: i64) {
        unsafe { StateApplyAction(self.state, action) }
    }

    pub fn action_to_string(&self, player: i32, action: i64) -> String {
        let mut length = 0;
        let c_buf: *mut c_char =
            unsafe { StateActionToString(self.state, player, action, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn to_string(&self) -> String {
        let mut length = 0;
        let c_buf: *mut c_char = unsafe { StateToString(self.state, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn observation_string(&self) -> String {
        let mut length = 0;
        let c_buf: *mut c_char = unsafe { StateObservationString(self.state, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn information_state_string(&self) -> String {
        let mut length = 0;
        let c_buf: *mut c_char = unsafe { StateInformationStateString(self.state, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn observation_tensor(&self) -> Vec<f32> {
        let length = unsafe { StateObservationTensorSize(self.state) as usize };
        let mut obs_vec = Vec::with_capacity(length);
        unsafe {
            StateObservationTensor(self.state, obs_vec.as_mut_ptr(), length as i32);
            obs_vec.set_len(length);
        }
        obs_vec
    }

    pub fn information_state_tensor(&self) -> Vec<f32> {
        let length = unsafe { StateInformationStateTensorSize(self.state) as usize };
        let mut infostate_vec = Vec::with_capacity(length);
        unsafe {
            StateInformationStateTensor(self.state, infostate_vec.as_mut_ptr(), length as i32);
            infostate_vec.set_len(length);
        }
        infostate_vec
    }
}

impl Drop for State {
    fn drop(&mut self) {
        unsafe { DeleteState(self.state) }
    }
}

unsafe impl Send for State {}
unsafe impl Sync for State {}

impl Game {
    pub fn new(game_name: &str) -> Self {
        let game_name = CString::new(game_name).unwrap();
        Self { game: unsafe { LoadGame(game_name.as_ptr()) } }
    }

    pub fn new_with_parameters(parameters: &GameParameters) -> Self {
        Self { game: unsafe { LoadGameFromParameters(parameters.params) } }
    }

    pub fn short_name(&self) -> String {
        let mut length = 0;
        let c_buf = unsafe { GameShortName(self.game, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn long_name(&self) -> String {
        let mut length = 0;
        let c_buf = unsafe { GameLongName(self.game, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn new_initial_state(&self) -> State {
        unsafe { State::new(GameNewInitialState(self.game)) }
    }

    pub fn num_players(&self) -> i32 {
        unsafe { GameNumPlayers(self.game) }
    }

    pub fn max_game_length(&self) -> i32 {
        unsafe { GameMaxGameLength(self.game) }
    }

    pub fn num_distinct_actions(&self) -> i32 {
        unsafe { GameNumDistinctActions(self.game) }
    }

    pub fn observation_shape(&self) -> Vec<i32> {
        let mut size = 0;
        let c_buf = unsafe { GameObservationTensorShape(self.game, &mut size) };
        unsafe {
            let vec = slice::from_raw_parts(c_buf, size as usize).to_vec();
            free(c_buf as *mut c_void);
            vec
        }
    }

    pub fn information_state_tensor_shape(&self) -> Vec<i32> {
        let mut size = 0;
        let c_buf = unsafe { GameInformationStateTensorShape(self.game, &mut size) };
        unsafe {
            let vec = slice::from_raw_parts(c_buf, size as usize).to_vec();
            free(c_buf as *mut c_void);
            vec
        }
    }
}

impl Drop for Game {
    fn drop(&mut self) {
        unsafe { DeleteGame(self.game) }
    }
}

unsafe impl Send for Game {}
unsafe impl Sync for Game {}

impl Bot {
    pub fn step(&mut self, state: &State) -> i64 {
        unsafe { BotStep(self.bot, state.state) }
    }

    pub fn inform_action(&mut self, state: &State, player_id: i32, action: i64) {
        unsafe { BotInformAction(self.bot, state.state, player_id, action) };
    }

    pub fn restart(&mut self) {
        unsafe { BotRestart(self.bot) };
    }
}

impl Drop for Bot {
    fn drop(&mut self) {
        unsafe { DeleteBot(self.bot) }
    }
}

unsafe impl Send for Bot {}
unsafe impl Sync for Bot {}

pub fn create_bot_by_name(
    bot_name: &str,
    game: &Game,
    player_id: i32,
    params: &GameParameters,
) -> Bot {
    let bot_name = CString::new(bot_name).unwrap();
    let bot = unsafe {
        BotRegistererCreateByName(bot_name.as_ptr(), game.game, player_id, params.params)
    };
    Bot { bot }
}
<commit_msg>Fix type of State::num_players() rust binding<commit_after>extern crate libc;

use libc::{c_char, free};
use std::ffi::CString;
use std::os::raw::c_void;
use std::slice;

mod open_spiel_bindings;
use open_spiel_bindings::*;

fn convert_and_free_cstring(c_buf: *mut c_char, len: u64) -> String {
    let bytes = unsafe { std::slice::from_raw_parts(c_buf as *const u8, len as usize) };
    let str_slice = unsafe { std::str::from_utf8_unchecked(bytes) };
    let str_buf: String = str_slice.to_owned();
    unsafe { free(c_buf as *mut c_void) };
    str_buf
}

pub struct GameParameters {
    params: *mut c_void,
}

pub struct State {
    state: *mut c_void,
}

pub struct Game {
    game: *mut c_void,
}

pub struct Bot {
    bot: *mut c_void,
}

impl Default for GameParameters {
    fn default() -> Self {
        Self { params: unsafe { NewGameParameters() } }
    }
}

impl GameParameters {
    pub fn set_int(&mut self, key: &str, value: i32) {
        let key = CString::new(key).unwrap();
        unsafe {
            GameParametersSetInt(self.params, key.as_ptr(), value);
        }
    }

    pub fn set_f64(&mut self, key: &str, value: f64) {
        let key = CString::new(key).unwrap();
        unsafe {
            GameParametersSetDouble(self.params, key.as_ptr(), value);
        }
    }

    pub fn set_str(&mut self, key: &str, value: &str) {
        let key = CString::new(key).unwrap();
        let value = CString::new(value).unwrap();
        unsafe {
            GameParametersSetString(self.params, key.as_ptr(), value.as_ptr());
        }
    }
}

impl Drop for GameParameters {
    fn drop(&mut self) {
        unsafe { DeleteGameParameters(self.params) }
    }
}

unsafe impl Send for GameParameters {}
unsafe impl Sync for GameParameters {}

impl State {
    pub fn new(sptr: *mut c_void) -> State {
        State { state: sptr }
    }

    pub fn current_player(&self) -> i32 {
        unsafe { StateCurrentPlayer(self.state) }
    }

    pub fn clone(&self) -> State {
        unsafe { State { state: StateClone(self.state) } }
    }

    pub fn is_chance_node(&self) -> bool {
        let ret = unsafe { StateIsChanceNode(self.state) };
        ret == 1
    }

    pub fn is_terminal(&self) -> bool {
        let ret = unsafe { StateIsTerminal(self.state) };
        ret == 1
    }

    pub fn num_players(&self) -> i32 {
        unsafe { StateNumPlayers(self.state) }
    }

    pub fn returns(&self) -> Vec<f64> {
        let length = self.num_players() as usize;
        let mut returns_vec = Vec::with_capacity(length);
        unsafe {
            StateReturns(self.state, returns_vec.as_mut_ptr());
            returns_vec.set_len(length);
        }
        returns_vec
    }

    pub fn player_return(&self, player: i32) -> f64 {
        unsafe { StatePlayerReturn(self.state, player) }
    }

    pub fn legal_actions(&self) -> Vec<i64> {
        let mut c_num_legal_moves = 0;
        let c_buf = unsafe { StateLegalActions(self.state, &mut c_num_legal_moves) };
        unsafe {
            let vec = slice::from_raw_parts(c_buf, c_num_legal_moves as usize).to_vec();
            free(c_buf as *mut c_void);
            vec
        }
    }

    pub fn chance_outcomes(&self) -> Vec<(i64, f64)> {
        let legal_actions: Vec<i64> = self.legal_actions();
        let mut size = 0;
        let c_buf = unsafe { StateChanceOutcomeProbs(self.state, &mut size) };
        let length = size as usize;
        let mut vec = vec![(0, 0.0); length];
        unsafe {
            let probs_slice = slice::from_raw_parts(c_buf, length);
            for i in 0..length {
                vec[i] = (legal_actions[i], probs_slice[i]);
            }
            free(c_buf as *mut c_void);
        }
        vec
    }

    pub fn apply_action(&self, action: i64) {
        unsafe { StateApplyAction(self.state, action) }
    }

    pub fn action_to_string(&self, player: i32, action: i64) -> String {
        let mut length = 0;
        let c_buf: *mut c_char =
            unsafe { StateActionToString(self.state, player, action, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn to_string(&self) -> String {
        let mut length = 0;
        let c_buf: *mut c_char = unsafe { StateToString(self.state, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn observation_string(&self) -> String {
        let mut length = 0;
        let c_buf: *mut c_char = unsafe { StateObservationString(self.state, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn information_state_string(&self) -> String {
        let mut length = 0;
        let c_buf: *mut c_char = unsafe { StateInformationStateString(self.state, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn observation_tensor(&self) -> Vec<f32> {
        let length = unsafe { StateObservationTensorSize(self.state) as usize };
        let mut obs_vec = Vec::with_capacity(length);
        unsafe {
            StateObservationTensor(self.state, obs_vec.as_mut_ptr(), length as i32);
            obs_vec.set_len(length);
        }
        obs_vec
    }

    pub fn information_state_tensor(&self) -> Vec<f32> {
        let length = unsafe { StateInformationStateTensorSize(self.state) as usize };
        let mut infostate_vec = Vec::with_capacity(length);
        unsafe {
            StateInformationStateTensor(self.state, infostate_vec.as_mut_ptr(), length as i32);
            infostate_vec.set_len(length);
        }
        infostate_vec
    }
}

impl Drop for State {
    fn drop(&mut self) {
        unsafe { DeleteState(self.state) }
    }
}

unsafe impl Send for State {}
unsafe impl Sync for State {}

impl Game {
    pub fn new(game_name: &str) -> Self {
        let game_name = CString::new(game_name).unwrap();
        Self { game: unsafe { LoadGame(game_name.as_ptr()) } }
    }

    pub fn new_with_parameters(parameters: &GameParameters) -> Self {
        Self { game: unsafe { LoadGameFromParameters(parameters.params) } }
    }

    pub fn short_name(&self) -> String {
        let mut length = 0;
        let c_buf = unsafe { GameShortName(self.game, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn long_name(&self) -> String {
        let mut length = 0;
        let c_buf = unsafe { GameLongName(self.game, &mut length) };
        convert_and_free_cstring(c_buf, length)
    }

    pub fn new_initial_state(&self) -> State {
        unsafe { State::new(GameNewInitialState(self.game)) }
    }

    pub fn num_players(&self) -> i32 {
        unsafe { GameNumPlayers(self.game) }
    }

    pub fn max_game_length(&self) -> i32 {
        unsafe { GameMaxGameLength(self.game) }
    }

    pub fn num_distinct_actions(&self) -> i32 {
        unsafe { GameNumDistinctActions(self.game) }
    }

    pub fn observation_shape(&self) -> Vec<i32> {
        let mut size = 0;
        let c_buf = unsafe { GameObservationTensorShape(self.game, &mut size) };
        unsafe {
            let vec = slice::from_raw_parts(c_buf, size as usize).to_vec();
            free(c_buf as *mut c_void);
            vec
        }
    }

    pub fn information_state_tensor_shape(&self) -> Vec<i32> {
        let mut size = 0;
        let c_buf = unsafe { GameInformationStateTensorShape(self.game, &mut size) };
        unsafe {
            let vec = slice::from_raw_parts(c_buf, size as usize).to_vec();
            free(c_buf as *mut c_void);
            vec
        }
    }
}

impl Drop for Game {
    fn drop(&mut self) {
        unsafe { DeleteGame(self.game) }
    }
}

unsafe impl Send for Game {}
unsafe impl Sync for Game {}

impl Bot {
    pub fn step(&mut self, state: &State) -> i64 {
        unsafe { BotStep(self.bot, state.state) }
    }

    pub fn inform_action(&mut self, state: &State, player_id: i32, action: i64) {
        unsafe { BotInformAction(self.bot, state.state, player_id, action) };
    }

    pub fn restart(&mut self) {
        unsafe { BotRestart(self.bot) };
    }
}

impl Drop for Bot {
    fn drop(&mut self) {
        unsafe { DeleteBot(self.bot) }
    }
}

unsafe impl Send for Bot {}
unsafe impl Sync for Bot {}

pub fn create_bot_by_name(
    bot_name: &str,
    game: &Game,
    player_id: i32,
    params: &GameParameters,
) -> Bot {
    let bot_name = CString::new(bot_name).unwrap();
    let bot = unsafe {
        BotRegistererCreateByName(bot_name.as_ptr(), game.game, player_id, params.params)
    };
    Bot { bot }
}
<|endoftext|>


<commit_before>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::JsonVal;
use rs_es::operations::search::{Sort, SortField, Order};

use terms::VectorOfTerms;

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: u32
}

pub struct Talent;

impl Talent {
  /// Return a `Vec<Filter>` with visibility criteria for the talents.
  /// If `presented_talents` is provided, talents who match the IDs
  /// contained there skip the standard visibility criteria.
  ///
  /// Basically, the talents must be accepted into the platform and must be
  /// inside a living batch to match the visibility criteria.
  fn visibility_filters(presented_talents: Vec<i32>) -> Vec<Filter> {
    let now = DateTime::timestamp(&UTC::now());

    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_starts_at")
                                             .with_lt(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_ends_at")
                                             .with_gte(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if presented_talents.is_empty() {
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  /// Given parameters inside the query string mapped inisde a `Map`,
  /// return a `Query` for ElasticSearch.
  ///
  /// The `VectorOfTerms` are ORred, while `Filter`s are ANDed.
  /// I.e.: given ["Fullstack", "DevOps"] as `work_roles`, found talents
  /// will present at least one of these roles), but both `work_roles`
  /// and `work_languages`, if provided, must not be empty.
  fn search_filters(params: &Map) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &vec_from_params!(params, "work_authorization")),

                                     Talent::visibility_filters(
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  /// Return a `Sort` that makes values be sorted for `updated_at`, descendently.
  fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("updated_at", Some(Order::Desc)).build()
      ])
  }

  /// Query ElasticSearch on given `indexes` and `params` and return the IDs of
  /// the found talents.
  pub fn search(mut es: Client, params: &Map, indexes: &[&str]) -> Vec<u32> {
    let result = es.search_query()
                   .with_indexes(indexes)
                   .with_query(&Talent::search_filters(params))
                   .with_sort(&Talent::sorting_criteria())
                   .send()
                   .ok()
                   .unwrap();

    result.hits.hits.into_iter()
                    .map(|hit| {
                      let talent: TalentsSearchResult = hit.source().unwrap();
                      talent.id
                    })
                    .collect::<Vec<u32>>()
  }
}

#[cfg(test)]
mod tests {
  #[test]
  fn test_visibility_filters() {
    // TODO
  }

  #[test]
  fn test_search_filters() {
    // TODO
  }

  #[test]
  fn test_search() {
    // TODO
  }
}
<commit_msg>resources/user: fix visibility filter's presented talents logic<commit_after>use chrono::UTC;
use chrono::datetime::DateTime;

use params::*;

use rs_es::Client;
use rs_es::query::{Filter, Query};
use rs_es::units::JsonVal;
use rs_es::operations::search::{Sort, SortField, Order};

use terms::VectorOfTerms;

#[derive(Debug, RustcDecodable)]
struct TalentsSearchResult {
  id: u32
}

pub struct Talent;

impl Talent {
  /// Return a `Vec<Filter>` with visibility criteria for the talents.
  /// If `presented_talents` is provided, talents who match the IDs
  /// contained there skip the standard visibility criteria.
  ///
  /// Basically, the talents must be accepted into the platform and must be
  /// inside a living batch to match the visibility criteria.
  fn visibility_filters(presented_talents: Vec<i32>) -> Vec<Filter> {
    let now = DateTime::timestamp(&UTC::now());

    let visibility_rules = Filter::build_bool()
                                  .with_must(
                                    vec![
                                      Filter::build_term("accepted", true)
                                             .build(),
                                      Filter::build_range("batch_starts_at")
                                             .with_lt(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build(),
                                      Filter::build_range("batch_ends_at")
                                             .with_gte(JsonVal::from(now))
                                             .with_format("epoch_second")
                                             .build()
                                    ])
                                  .build();

    if !presented_talents.is_empty() {
      let presented_talents_filters = Filter::build_bool()
                                             .with_must(
                                               vec![
                                                 <Filter as VectorOfTerms<i32>>::build_terms(
                                                   "ids", &presented_talents)
                                               ].into_iter()
                                                .flat_map(|x| x)
                                                .collect::<Vec<Filter>>())
                                             .build();
      vec![
        Filter::build_bool()
               .with_should(vec![visibility_rules, presented_talents_filters])
               .build()
      ]
    }
    else {
      vec![visibility_rules]
    }
  }

  /// Given parameters inside the query string mapped inisde a `Map`,
  /// return a `Query` for ElasticSearch.
  ///
  /// The `VectorOfTerms` are ORred, while `Filter`s are ANDed.
  /// I.e.: given ["Fullstack", "DevOps"] as `work_roles`, found talents
  /// will present at least one of these roles), but both `work_roles`
  /// and `work_languages`, if provided, must not be empty.
  fn search_filters(params: &Map) -> Query {
    let company_id = i32_vec_from_params!(params, "company_id");

    Query::build_filtered(Filter::build_bool()
                                 .with_must(
                                   vec![
                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_roles", &vec_from_params!(params, "work_roles")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_languages", &vec_from_params!(params, "work_languages")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_experience", &vec_from_params!(params, "work_experience")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                       "work_locations", &vec_from_params!(params, "work_locations")),

                                     <Filter as VectorOfTerms<String>>::build_terms(
                                      "work_authorization", &vec_from_params!(params, "work_authorization")),

                                     Talent::visibility_filters(
                                       i32_vec_from_params!(params, "presented_talents"))
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .with_must_not(
                                   vec![
                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "company_ids", &company_id),

                                     <Filter as VectorOfTerms<i32>>::build_terms(
                                       "blocked_companies", &company_id)
                                   ].into_iter()
                                    .flat_map(|x| x)
                                    .collect::<Vec<Filter>>())
                                 .build())
          .build()
  }

  /// Return a `Sort` that makes values be sorted for `updated_at`, descendently.
  fn sorting_criteria() -> Sort {
    Sort::new(
      vec![
        SortField::new("updated_at", Some(Order::Desc)).build()
      ])
  }

  /// Query ElasticSearch on given `indexes` and `params` and return the IDs of
  /// the found talents.
  pub fn search(mut es: Client, params: &Map, indexes: &[&str]) -> Vec<u32> {
    let result = es.search_query()
                   .with_indexes(indexes)
                   .with_query(&Talent::search_filters(params))
                   .with_sort(&Talent::sorting_criteria())
                   .send()
                   .ok()
                   .unwrap();

    result.hits.hits.into_iter()
                    .map(|hit| {
                      let talent: TalentsSearchResult = hit.source().unwrap();
                      talent.id
                    })
                    .collect::<Vec<u32>>()
  }
}

#[cfg(test)]
mod tests {
  #[test]
  fn test_visibility_filters() {
    // TODO
  }

  #[test]
  fn test_search_filters() {
    // TODO
  }

  #[test]
  fn test_search() {
    // TODO
  }
}
<|endoftext|>