<commit_before>//! Contains the routing logic for the website.

// FIXME: maplit creates a *lot* of these warnings.
#![cfg_attr(feature="clippy", allow(used_underscore_binding))]

use std::fs;
use std::path::Path;

use chrono::Datelike;
use hbs::Template;
use iron::prelude::*;
use iron::status;
use persistent::Read;
use router::Router;

use blog::{self, Post, Summary};
use persistence::Projects;

const RESUME_LINK: &'static str =
    r"https://github.com/euclio/resume/blob/master/resume.pdf?raw=true";

fn generate_blog_post_summaries() -> Vec<Summary> {
    let mut posts = blog::parse_posts(Path::new("blog/")).unwrap();
    posts.sort_by(|a, b| b.date.cmp(&a.date));
    posts.into_iter()
         .map(|p| p.to_summary())
         .collect::<Vec<_>>()
}

fn resume(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let data = btreemap!{
        "resume_link" => RESUME_LINK,
    };
    res.set_mut(Template::new("resume", data)).set_mut(status::Ok);
    Ok(res)
}

fn projects(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let projects = req.get::<Read<Projects>>().unwrap();

    let data = btreemap!{
        "projects" => projects,
    };
    res.set_mut(Template::new("projects", data)).set_mut(status::Ok);
    Ok(res)
}

fn blog_post(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let year = req.extensions.get::<Router>().unwrap().find("year").unwrap();
    let month = req.extensions.get::<Router>().unwrap().find("month").unwrap();
    let day = req.extensions.get::<Router>().unwrap().find("day").unwrap();
    let title = req.extensions.get::<Router>().unwrap().find("title").unwrap();

    let post = blog::parse_posts(Path::new("blog/"))
                   .unwrap()
                   .iter()
                   .find(|post| {
                       post.date.year() == year.parse().unwrap() &&
                       post.date.month() == month.parse().unwrap() &&
                       post.date.day() == day.parse().unwrap() &&
                       post.title.to_lowercase().replace(" ", "-") == title
                   })
                   .unwrap()
                   .to_owned();

    let data = btreemap!{
        "title" => post.title.to_owned(),
        "date" => post.date.format(Post::DATE_FORMAT).to_string(),
        "content" => post.html.to_string(),
    };
    res.set_mut(Template::new("blog_post", data)).set_mut(status::Ok);

    Ok(res)
}

fn blog(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let data = btreemap!{
        "posts" => generate_blog_post_summaries(),
    };
    res.set_mut(Template::new("blog", data)).set_mut(status::Ok);
    Ok(res)
}

fn about(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let images = Path::new("static/images/slideshow");
    let image_urls = fs::read_dir(images)
                         .unwrap()
                         .into_iter()
                         .map(|path| path.unwrap().path().to_str().unwrap().to_owned())
                         .collect::<Vec<_>>();

    let data = btreemap! {
        "image_urls" => image_urls,
    };
    res.set_mut(Template::new("about", data)).set_mut(status::Ok);
    Ok(res)
}

fn index(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let summaries = generate_blog_post_summaries();

    let data = btreemap!{
        "posts" => summaries.iter().take(3).collect::<Vec<_>>(),
    };
    res.set_mut(Template::new("index", data)).set_mut(status::Ok);
    Ok(res)
}

/// Returns the router for the server.
pub fn get_router() -> Router {
    router!(
        get "/" => index,
        get "/about" => about,
        get "/blog" => blog,
        get "/blog/:year/:month/:day/:title" => blog_post,
        get "/projects" => projects,
        get "/resume" => resume,
    )
}
<commit_msg>fix(blog): return 404 on bad blog links<commit_after>//! Contains the routing logic for the website.

// FIXME: maplit creates a *lot* of these warnings.
#![cfg_attr(feature="clippy", allow(used_underscore_binding))]

use std::fs;
use std::path::Path;

use chrono::NaiveDate;
use hbs::Template;
use iron::prelude::*;
use iron::status;
use persistent::Read;
use router::{Router, NoRoute};

use blog::{self, Post, Summary};
use persistence::Projects;

const RESUME_LINK: &'static str =
    r"https://github.com/euclio/resume/blob/master/resume.pdf?raw=true";

fn generate_blog_post_summaries() -> Vec<Summary> {
    let mut posts = blog::parse_posts(Path::new("blog/")).unwrap();
    posts.sort_by(|a, b| b.date.cmp(&a.date));
    posts.into_iter()
         .map(|p| p.to_summary())
         .collect::<Vec<_>>()
}

fn resume(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let data = btreemap!{
        "resume_link" => RESUME_LINK,
    };
    res.set_mut(Template::new("resume", data)).set_mut(status::Ok);
    Ok(res)
}

fn projects(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let projects = req.get::<Read<Projects>>().unwrap();

    let data = btreemap!{
        "projects" => projects,
    };
    res.set_mut(Template::new("projects", data)).set_mut(status::Ok);
    Ok(res)
}

fn blog_post(req: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let year = req.extensions.get::<Router>().unwrap().find("year");
    let month = req.extensions.get::<Router>().unwrap().find("month");
    let day = req.extensions.get::<Router>().unwrap().find("day");
    let title = req.extensions.get::<Router>().unwrap().find("title");

    let post = blog::parse_posts(Path::new("blog/"))
                   .unwrap()
                   .into_iter()
                   .find(|post| {
                       let year = year.and_then(|y| y.parse().ok());
                       let month = month.and_then(|m| m.parse().ok());
                       let day = day.and_then(|d| d.parse().ok());

                       if let (Some(year), Some(month), Some(day)) = (year, month, day) {
                           let title_match = match title {
                               Some(title) => post.title.to_lowercase().replace(" ", "-") == title,
                               None => false,
                           };

                           let requested_date = NaiveDate::from_ymd(year, month, day);
                           post.date.naive_utc().date() == requested_date && title_match
                       } else {
                           false
                       }
                   });

    match post {
        Some(ref post) => {
            let data = btreemap!{
                "title" => post.title.to_owned(),
                "date" => post.date.format(Post::DATE_FORMAT).to_string(),
                "content" => post.html.to_string(),
            };
            res.set_mut(Template::new("blog_post", data)).set_mut(status::Ok);
            Ok(res)
        }
        None => Err(IronError::new(NoRoute, status::NotFound)),
    }
}

fn blog(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let data = btreemap!{
        "posts" => generate_blog_post_summaries(),
    };
    res.set_mut(Template::new("blog", data)).set_mut(status::Ok);
    Ok(res)
}

fn about(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let images = Path::new("static/images/slideshow");
    let image_urls = fs::read_dir(images)
                         .unwrap()
                         .into_iter()
                         .map(|path| path.unwrap().path().to_str().unwrap().to_owned())
                         .collect::<Vec<_>>();

    let data = btreemap! {
        "image_urls" => image_urls,
    };
    res.set_mut(Template::new("about", data)).set_mut(status::Ok);
    Ok(res)
}

fn index(_: &mut Request) -> IronResult<Response> {
    let mut res = Response::new();

    let summaries = generate_blog_post_summaries();

    let data = btreemap!{
        "posts" => summaries.iter().take(3).collect::<Vec<_>>(),
    };
    res.set_mut(Template::new("index", data)).set_mut(status::Ok);
    Ok(res)
}

/// Returns the router for the server.
pub fn get_router() -> Router {
    router!(
        get "/" => index,
        get "/about" => about,
        get "/blog" => blog,
        get "/blog/:year/:month/:day/:title" => blog_post,
        get "/projects" => projects,
        get "/resume" => resume,
    )
}
<|endoftext|>


<commit_before><commit_msg>Problem: using slice references in script stacks is problematic<commit_after><|endoftext|>


<commit_before><commit_msg>Use uniform convention on column field names<commit_after><|endoftext|>


<commit_before>pub struct Vec3 {
  pub i : f64,
  pub j : f64,
  pub k : f64,
}

impl Vec3 {
  pub fn length(&self) -> f64 {
    (self.i * self.i + self.j * self.j + self.k * self.k).sqrt()
  }
}
<commit_msg>Implement traits for vector addition and subtraction<commit_after>use std::ops::{Add, Sub};

pub struct Vec3 {
  pub i : f64,
  pub j : f64,
  pub k : f64,
}

impl Vec3 {
  pub fn length(&self) -> f64 {
    (self.i * self.i + self.j * self.j + self.k * self.k).sqrt()
  }
}

impl Add for Vec3 {
  type Output = Vec3;
  fn add(self, other: Vec3) -> Vec3 {
      Vec3 {i: self.i + other.i, j: self.j + other.j, k: self.k + other.k}
  }
}

impl Sub for Vec3 {
  type Output = Vec3;
  fn sub(self, other: Vec3) -> Vec3 {
      Vec3 {i: self.i - other.i, j: self.j - other.j, k: self.k - other.k}
  }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use serde_json;
use directory::error::{OpenReadError, DeleteError, OpenWriteError};
use directory::{ReadOnlySource, WritePtr};
use std::result;
use std::io;
use Directory;
use std::sync::{Arc, RwLock};
use std::collections::HashSet;
use std::sync::RwLockWriteGuard;
use std::io::Write;
use core::MANAGED_FILEPATH;
use std::collections::HashMap;
use std::fmt;
use Result;
use Error;

/// Wrapper of directories that keeps track of files created by Tantivy.
///
/// A managed directory is just a wrapper of a directory
/// that keeps a (persisted) list of the files that
/// have been created (and not deleted) by tantivy so far.
///
/// Thanks to this list, it implements a `garbage_collect` method
/// that removes the files that were created by tantivy and are not
/// useful anymore.
#[derive(Debug)]
pub struct ManagedDirectory {
    directory: Box<Directory>,
    meta_informations: Arc<RwLock<MetaInformation>>,
}

#[derive(Debug, Default)]
struct MetaInformation {
    managed_paths: HashSet<PathBuf>,
    protected_files: HashMap<PathBuf, usize>,
}

/// A `FileProtection` prevents the garbage collection of a file.
///
/// See `ManagedDirectory.protect_file_from_delete`.
pub struct FileProtection {
    directory: ManagedDirectory,
    path: PathBuf,
}

fn unprotect_file_from_delete(directory: &ManagedDirectory, path: &Path) {
    let mut meta_informations_wlock = directory.meta_informations
        .write()
        .expect("Managed file lock poisoned");
    if let Some(counter_ref_mut) = meta_informations_wlock
        .protected_files
        .get_mut(path) {
        (*counter_ref_mut) -= 1;
    }
}

impl fmt::Debug for FileProtection {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(formatter, "FileProtectionFor({:?})", self.path)
    }
}

impl Drop for FileProtection {
    fn drop(&mut self) {
        unprotect_file_from_delete(&self.directory, &*self.path);
    }
}

/// Saves the file containing the list of existing files
/// that were created by tantivy.
fn save_managed_paths(directory: &mut Directory, wlock: &RwLockWriteGuard<MetaInformation>) -> io::Result<()> {
    let managed_paths = wlock.managed_paths.clone();
    let mut w = serde_json::to_vec(&managed_paths)?;
    write!(&mut w, "\n")?;
    directory.atomic_write(&MANAGED_FILEPATH, &w[..])?;
    Ok(())
}

impl ManagedDirectory {

    /// Wraps a directory as managed directory.
    pub fn new<Dir: Directory>(directory: Dir) -> Result<ManagedDirectory> {
        match directory.atomic_read(&MANAGED_FILEPATH) {
            Ok(data) => {
                let managed_files_json = String::from_utf8_lossy(&data);
                let managed_files: HashSet<PathBuf> = serde_json::from_str(&managed_files_json)
                    .map_err(|e| Error::CorruptedFile(MANAGED_FILEPATH.clone(), Box::new(e)))?;
                Ok(ManagedDirectory {
                    directory: box directory,
                    meta_informations: Arc::new(RwLock::new(
                        MetaInformation {
                            managed_paths: managed_files,
                            protected_files: HashMap::default()
                        })),
                })
            }
            Err(OpenReadError::FileDoesNotExist(_)) => {
                Ok(ManagedDirectory {
                    directory: box directory,
                    meta_informations: Arc::default(),
                })
            }
            Err(OpenReadError::IOError(e)) => {
                Err(From::from(e))
            }
        }
    }

    /// Garbage collect unused files.
    ///
    /// Removes the files that were created by `tantivy` and are not
    /// used by any segment anymore.
    ///
    /// * `living_files` - List of files that are still used by the index.
    ///
    /// This method does not panick nor returns errors.
    /// If a file cannot be deleted (for permission reasons for instance)
    /// an error is simply logged, and the file remains in the list of managed
    /// files.
    pub fn garbage_collect(&mut self, living_files: HashSet<PathBuf>) {
        let mut files_to_delete = vec!();
        {   // releasing the lock as .delete() will use it too.
            let meta_informations_rlock = self.meta_informations
                .read()
                .expect("Managed directory rlock poisoned in garbage collect.");
            for managed_path in &meta_informations_rlock.managed_paths {
                if !living_files.contains(managed_path) {
                    files_to_delete.push(managed_path.clone());
                }
            }
        }

        let mut deleted_files = vec!();
        {
            for file_to_delete in files_to_delete {
                match self.delete(&file_to_delete) {
                    Ok(_) => {
                        info!("Deleted {:?}", file_to_delete);
                        deleted_files.push(file_to_delete);
                    }
                    Err(file_error) => {
                        error!("Failed to delete {:?}", file_to_delete);
                        match file_error {
                            DeleteError::FileDoesNotExist(_) => {
                                deleted_files.push(file_to_delete);
                            }
                            DeleteError::IOError(_) => {
                                if !cfg!(target_os = "windows") {
                                    error!("Failed to delete {:?}", file_to_delete);
                                }
                            }
                            DeleteError::FileProtected(_) => {
                                // this is expected.
                            }
                        }

                    }
                }
            }
        }

        if !deleted_files.is_empty() {
            // update the list of managed files by removing
            // the file that were removed.
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed directory wlock poisoned (2).");
            {
                let managed_paths_write = &mut meta_informations_wlock.managed_paths;
                for delete_file in &deleted_files {
                    managed_paths_write.remove(delete_file);
                }
            }
            if let Err(_) = save_managed_paths(self.directory.as_mut(), &meta_informations_wlock) {
                error!("Failed to save the list of managed files.");
            }
        }

    }

    /// Protects a file from being garbage collected.
    ///
    /// The method returns a `FileProtection` object.
    /// The file will not be garbage collected as long as the
    /// `FileProtection` object is kept alive.
    pub fn protect_file_from_delete(&self, path: &Path) -> FileProtection {
        let pathbuf = path.to_owned();
        {
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed file lock poisoned on protect");
            *meta_informations_wlock
                .protected_files
                .entry(pathbuf.clone())
                .or_insert(0) += 1;
        }
        FileProtection {
            directory: self.clone(),
            path: pathbuf.clone(),
        }
    }

    /// Registers a file as managed
    ///
    /// This method must be called before the file is
    /// actually created to ensure that a failure between
    /// registering the filepath and creating the file
    /// will not lead to garbage files that will
    /// never get removed.
    fn register_file_as_managed(&mut self, filepath: &Path) -> io::Result<()> {
        let mut meta_wlock = self.meta_informations
                .write()
                .expect("Managed file lock poisoned");
        let has_changed = meta_wlock.managed_paths.insert(filepath.to_owned());
        if has_changed {
            save_managed_paths(self.directory.as_mut(), &meta_wlock)?;
        }
        Ok(())
    }
}

impl Directory for ManagedDirectory {

    fn open_read(&self, path: &Path) -> result::Result<ReadOnlySource, OpenReadError> {
        self.directory.open_read(path)
    }

    fn open_write(&mut self, path: &Path) -> result::Result<WritePtr, OpenWriteError> {
        self.register_file_as_managed(path)?;
        self.directory.open_write(path)
    }

    fn atomic_write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
        self.register_file_as_managed(path)?;
        self.directory.atomic_write(path, data)
    }

    fn atomic_read(&self, path: &Path) -> result::Result<Vec<u8>, OpenReadError> {
        self.directory.atomic_read(path)
    }

    fn delete(&self, path: &Path) -> result::Result<(), DeleteError> {
        {
            let metas_rlock = self.meta_informations
                .read()
                .expect("poisoned lock in managed directory meta");
            if let Some(counter) = metas_rlock.protected_files.get(path) {
                if *counter > 0 {
                    return Err(DeleteError::FileProtected(path.to_owned()))
                }
            }
        }
        self.directory.delete(path)
    }

    fn exists(&self, path: &Path) -> bool {
        self.directory.exists(path)
    }

    fn box_clone(&self) -> Box<Directory> {
        box self.clone()
    }

}

impl Clone for ManagedDirectory {
    fn clone(&self) -> ManagedDirectory {
        ManagedDirectory {
            directory: self.directory.box_clone(),
            meta_informations: self.meta_informations.clone(),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use directory::MmapDirectory;
    use std::path::Path;
    use std::io::Write;
    use tempdir::TempDir;

    lazy_static! {
        static ref TEST_PATH1: &'static Path = Path::new("some_path_for_test");
        static ref TEST_PATH2: &'static Path = Path::new("some_path_for_test2");
    }

    #[test]
    fn test_managed_directory() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                let mut write_file = managed_directory.open_write(*TEST_PATH1).unwrap();
                write_file.flush().unwrap();
            }
            {
                managed_directory.atomic_write(*TEST_PATH2, &vec!(0u8,1u8)).unwrap();
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> = [TEST_PATH1.to_owned()]
                    .into_iter()
                    .cloned()
                    .collect();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> = HashSet::new();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(!managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
    }

    #[test]
    fn test_managed_directory_gc_while_mmapped() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory.atomic_write(*TEST_PATH1, &vec!(0u8,1u8)).unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        let _mmap_read = managed_directory.open_read(*TEST_PATH1).unwrap();
        managed_directory.garbage_collect(living_files.clone());
        if cfg!(target_os = "windows") {
            // On Windows, gc should try and fail the file as it is mmapped.
            assert!(managed_directory.exists(*TEST_PATH1));
            // unmap should happen here.
            drop(_mmap_read);
            // The file should still be in the list of managed file and
            // eventually be deleted once mmap is released.
            managed_directory.garbage_collect(living_files);
            assert!(!managed_directory.exists(*TEST_PATH1));
        }
        else {
            assert!(!managed_directory.exists(*TEST_PATH1));
        }

    }

    #[test]
    fn test_managed_directory_protect() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory.atomic_write(*TEST_PATH1, &vec!(0u8,1u8)).unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        {
            let _file_protection = managed_directory.protect_file_from_delete(*TEST_PATH1);
            managed_directory.garbage_collect(living_files.clone());
            assert!(managed_directory.exists(*TEST_PATH1));
        }

        managed_directory.garbage_collect(living_files.clone());
        assert!(!managed_directory.exists(*TEST_PATH1));

    }

}
<commit_msg>Avoid clone() call<commit_after>use std::path::{Path, PathBuf};
use serde_json;
use directory::error::{OpenReadError, DeleteError, OpenWriteError};
use directory::{ReadOnlySource, WritePtr};
use std::result;
use std::io;
use Directory;
use std::sync::{Arc, RwLock};
use std::collections::HashSet;
use std::sync::RwLockWriteGuard;
use std::io::Write;
use core::MANAGED_FILEPATH;
use std::collections::HashMap;
use std::fmt;
use Result;
use Error;

/// Wrapper of directories that keeps track of files created by Tantivy.
///
/// A managed directory is just a wrapper of a directory
/// that keeps a (persisted) list of the files that
/// have been created (and not deleted) by tantivy so far.
///
/// Thanks to this list, it implements a `garbage_collect` method
/// that removes the files that were created by tantivy and are not
/// useful anymore.
#[derive(Debug)]
pub struct ManagedDirectory {
    directory: Box<Directory>,
    meta_informations: Arc<RwLock<MetaInformation>>,
}

#[derive(Debug, Default)]
struct MetaInformation {
    managed_paths: HashSet<PathBuf>,
    protected_files: HashMap<PathBuf, usize>,
}

/// A `FileProtection` prevents the garbage collection of a file.
///
/// See `ManagedDirectory.protect_file_from_delete`.
pub struct FileProtection {
    directory: ManagedDirectory,
    path: PathBuf,
}

fn unprotect_file_from_delete(directory: &ManagedDirectory, path: &Path) {
    let mut meta_informations_wlock = directory.meta_informations
        .write()
        .expect("Managed file lock poisoned");
    if let Some(counter_ref_mut) = meta_informations_wlock
        .protected_files
        .get_mut(path) {
        (*counter_ref_mut) -= 1;
    }
}

impl fmt::Debug for FileProtection {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(formatter, "FileProtectionFor({:?})", self.path)
    }
}

impl Drop for FileProtection {
    fn drop(&mut self) {
        unprotect_file_from_delete(&self.directory, &*self.path);
    }
}

/// Saves the file containing the list of existing files
/// that were created by tantivy.
fn save_managed_paths(directory: &mut Directory, wlock: &RwLockWriteGuard<MetaInformation>) -> io::Result<()> {
    let mut w = serde_json::to_vec(&wlock.managed_paths)?;
    write!(&mut w, "\n")?;
    directory.atomic_write(&MANAGED_FILEPATH, &w[..])?;
    Ok(())
}

impl ManagedDirectory {

    /// Wraps a directory as managed directory.
    pub fn new<Dir: Directory>(directory: Dir) -> Result<ManagedDirectory> {
        match directory.atomic_read(&MANAGED_FILEPATH) {
            Ok(data) => {
                let managed_files_json = String::from_utf8_lossy(&data);
                let managed_files: HashSet<PathBuf> = serde_json::from_str(&managed_files_json)
                    .map_err(|e| Error::CorruptedFile(MANAGED_FILEPATH.clone(), Box::new(e)))?;
                Ok(ManagedDirectory {
                    directory: box directory,
                    meta_informations: Arc::new(RwLock::new(
                        MetaInformation {
                            managed_paths: managed_files,
                            protected_files: HashMap::default()
                        })),
                })
            }
            Err(OpenReadError::FileDoesNotExist(_)) => {
                Ok(ManagedDirectory {
                    directory: box directory,
                    meta_informations: Arc::default(),
                })
            }
            Err(OpenReadError::IOError(e)) => {
                Err(From::from(e))
            }
        }
    }

    /// Garbage collect unused files.
    ///
    /// Removes the files that were created by `tantivy` and are not
    /// used by any segment anymore.
    ///
    /// * `living_files` - List of files that are still used by the index.
    ///
    /// This method does not panick nor returns errors.
    /// If a file cannot be deleted (for permission reasons for instance)
    /// an error is simply logged, and the file remains in the list of managed
    /// files.
    pub fn garbage_collect(&mut self, living_files: HashSet<PathBuf>) {
        let mut files_to_delete = vec!();
        {   // releasing the lock as .delete() will use it too.
            let meta_informations_rlock = self.meta_informations
                .read()
                .expect("Managed directory rlock poisoned in garbage collect.");
            for managed_path in &meta_informations_rlock.managed_paths {
                if !living_files.contains(managed_path) {
                    files_to_delete.push(managed_path.clone());
                }
            }
        }

        let mut deleted_files = vec!();
        {
            for file_to_delete in files_to_delete {
                match self.delete(&file_to_delete) {
                    Ok(_) => {
                        info!("Deleted {:?}", file_to_delete);
                        deleted_files.push(file_to_delete);
                    }
                    Err(file_error) => {
                        error!("Failed to delete {:?}", file_to_delete);
                        match file_error {
                            DeleteError::FileDoesNotExist(_) => {
                                deleted_files.push(file_to_delete);
                            }
                            DeleteError::IOError(_) => {
                                if !cfg!(target_os = "windows") {
                                    error!("Failed to delete {:?}", file_to_delete);
                                }
                            }
                            DeleteError::FileProtected(_) => {
                                // this is expected.
                            }
                        }

                    }
                }
            }
        }

        if !deleted_files.is_empty() {
            // update the list of managed files by removing
            // the file that were removed.
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed directory wlock poisoned (2).");
            {
                let managed_paths_write = &mut meta_informations_wlock.managed_paths;
                for delete_file in &deleted_files {
                    managed_paths_write.remove(delete_file);
                }
            }
            if let Err(_) = save_managed_paths(self.directory.as_mut(), &meta_informations_wlock) {
                error!("Failed to save the list of managed files.");
            }
        }

    }

    /// Protects a file from being garbage collected.
    ///
    /// The method returns a `FileProtection` object.
    /// The file will not be garbage collected as long as the
    /// `FileProtection` object is kept alive.
    pub fn protect_file_from_delete(&self, path: &Path) -> FileProtection {
        let pathbuf = path.to_owned();
        {
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed file lock poisoned on protect");
            *meta_informations_wlock
                .protected_files
                .entry(pathbuf.clone())
                .or_insert(0) += 1;
        }
        FileProtection {
            directory: self.clone(),
            path: pathbuf.clone(),
        }
    }

    /// Registers a file as managed
    ///
    /// This method must be called before the file is
    /// actually created to ensure that a failure between
    /// registering the filepath and creating the file
    /// will not lead to garbage files that will
    /// never get removed.
    fn register_file_as_managed(&mut self, filepath: &Path) -> io::Result<()> {
        let mut meta_wlock = self.meta_informations
                .write()
                .expect("Managed file lock poisoned");
        let has_changed = meta_wlock.managed_paths.insert(filepath.to_owned());
        if has_changed {
            save_managed_paths(self.directory.as_mut(), &meta_wlock)?;
        }
        Ok(())
    }
}

impl Directory for ManagedDirectory {

    fn open_read(&self, path: &Path) -> result::Result<ReadOnlySource, OpenReadError> {
        self.directory.open_read(path)
    }

    fn open_write(&mut self, path: &Path) -> result::Result<WritePtr, OpenWriteError> {
        self.register_file_as_managed(path)?;
        self.directory.open_write(path)
    }

    fn atomic_write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
        self.register_file_as_managed(path)?;
        self.directory.atomic_write(path, data)
    }

    fn atomic_read(&self, path: &Path) -> result::Result<Vec<u8>, OpenReadError> {
        self.directory.atomic_read(path)
    }

    fn delete(&self, path: &Path) -> result::Result<(), DeleteError> {
        {
            let metas_rlock = self.meta_informations
                .read()
                .expect("poisoned lock in managed directory meta");
            if let Some(counter) = metas_rlock.protected_files.get(path) {
                if *counter > 0 {
                    return Err(DeleteError::FileProtected(path.to_owned()))
                }
            }
        }
        self.directory.delete(path)
    }

    fn exists(&self, path: &Path) -> bool {
        self.directory.exists(path)
    }

    fn box_clone(&self) -> Box<Directory> {
        box self.clone()
    }

}

impl Clone for ManagedDirectory {
    fn clone(&self) -> ManagedDirectory {
        ManagedDirectory {
            directory: self.directory.box_clone(),
            meta_informations: self.meta_informations.clone(),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use directory::MmapDirectory;
    use std::path::Path;
    use std::io::Write;
    use tempdir::TempDir;

    lazy_static! {
        static ref TEST_PATH1: &'static Path = Path::new("some_path_for_test");
        static ref TEST_PATH2: &'static Path = Path::new("some_path_for_test2");
    }

    #[test]
    fn test_managed_directory() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                let mut write_file = managed_directory.open_write(*TEST_PATH1).unwrap();
                write_file.flush().unwrap();
            }
            {
                managed_directory.atomic_write(*TEST_PATH2, &vec!(0u8,1u8)).unwrap();
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> = [TEST_PATH1.to_owned()]
                    .into_iter()
                    .cloned()
                    .collect();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> = HashSet::new();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(!managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
    }

    #[test]
    fn test_managed_directory_gc_while_mmapped() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory.atomic_write(*TEST_PATH1, &vec!(0u8,1u8)).unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        let _mmap_read = managed_directory.open_read(*TEST_PATH1).unwrap();
        managed_directory.garbage_collect(living_files.clone());
        if cfg!(target_os = "windows") {
            // On Windows, gc should try and fail the file as it is mmapped.
            assert!(managed_directory.exists(*TEST_PATH1));
            // unmap should happen here.
            drop(_mmap_read);
            // The file should still be in the list of managed file and
            // eventually be deleted once mmap is released.
            managed_directory.garbage_collect(living_files);
            assert!(!managed_directory.exists(*TEST_PATH1));
        }
        else {
            assert!(!managed_directory.exists(*TEST_PATH1));
        }

    }

    #[test]
    fn test_managed_directory_protect() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory.atomic_write(*TEST_PATH1, &vec!(0u8,1u8)).unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        {
            let _file_protection = managed_directory.protect_file_from_delete(*TEST_PATH1);
            managed_directory.garbage_collect(living_files.clone());
            assert!(managed_directory.exists(*TEST_PATH1));
        }

        managed_directory.garbage_collect(living_files.clone());
        assert!(!managed_directory.exists(*TEST_PATH1));

    }

}
<|endoftext|>


<commit_before>use serde::de::{Deserialize, Deserializer, Error as DeError};
use serde::ser::{Serialize, Serializer};

use crate::json::{from_value, JsonMap, Value};
use crate::model::channel::ReactionType;

/// The type of a component
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
#[repr(u8)]
pub enum ComponentType {
    ActionRow = 1,
    Button = 2,
    SelectMenu = 3,
    InputText = 4,
    Unknown = !0,
}

enum_number!(ComponentType {
    ActionRow,
    Button,
    SelectMenu,
    InputText
});

/// An action row.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ActionRow {
    /// The type of component this ActionRow is.
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// The components of this ActionRow.
    #[serde(default)]
    pub components: Vec<ActionRowComponent>,
}

// A component which can be inside of an [`ActionRow`].
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ActionRowComponent {
    Button(Button),
    SelectMenu(SelectMenu),
    InputText(InputText),
}

impl<'de> Deserialize<'de> for ActionRowComponent {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let map = JsonMap::deserialize(deserializer)?;

        let kind = map
            .get("type")
            .ok_or_else(|| DeError::custom("expected type"))
            .and_then(ComponentType::deserialize)
            .map_err(DeError::custom)?;

        match kind {
            ComponentType::Button => from_value::<Button>(Value::from(map))
                .map(ActionRowComponent::Button)
                .map_err(DeError::custom),
            ComponentType::SelectMenu => from_value::<SelectMenu>(Value::from(map))
                .map(ActionRowComponent::SelectMenu)
                .map_err(DeError::custom),
            ComponentType::InputText => from_value::<InputText>(Value::from(map))
                .map(ActionRowComponent::InputText)
                .map_err(DeError::custom),
            _ => Err(DeError::custom("Unknown component type")),
        }
    }
}

impl Serialize for ActionRowComponent {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::Button(c) => Button::serialize(c, serializer),
            Self::SelectMenu(c) => SelectMenu::serialize(c, serializer),
            Self::InputText(c) => InputText::serialize(c, serializer),
        }
    }
}

impl From<Button> for ActionRowComponent {
    fn from(component: Button) -> Self {
        ActionRowComponent::Button(component)
    }
}

impl From<SelectMenu> for ActionRowComponent {
    fn from(component: SelectMenu) -> Self {
        ActionRowComponent::SelectMenu(component)
    }
}

/// A button component.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Button {
    /// The component type, it will always be [`ComponentType::Button`].
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// The button style.
    pub style: ButtonStyle,
    /// The text which appears on the button.
    pub label: Option<String>,
    /// The emoji of this button, if there is one.
    pub emoji: Option<ReactionType>,
    /// An identifier defined by the developer for the button.
    pub custom_id: Option<String>,
    /// The url of the button, if there is one.
    pub url: Option<String>,
    /// Whether the button is disabled.
    #[serde(default)]
    pub disabled: bool,
}

/// The style of a button.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
#[repr(u8)]
pub enum ButtonStyle {
    Primary = 1,
    Secondary = 2,
    Success = 3,
    Danger = 4,
    Link = 5,
    Unknown = !0,
}

enum_number!(ButtonStyle {
    Primary,
    Secondary,
    Success,
    Danger,
    Link
});

/// A select menu component.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SelectMenu {
    /// The component type, it will always be [`ComponentType::SelectMenu`].
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// The placeholder shown when nothing is selected.
    pub placeholder: Option<String>,
    /// An identifier defined by the developer for the select menu.
    pub custom_id: Option<String>,
    /// The minimum number of selections allowed.
    pub min_values: Option<u64>,
    /// The maximum number of selections allowed.
    pub max_values: Option<u64>,
    /// The options of this select menu.
    #[serde(default)]
    pub options: Vec<SelectMenuOption>,
}

/// A select menu component options.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SelectMenuOption {
    /// The text displayed on this option.
    pub label: String,
    /// The value to be sent for this option.
    pub value: String,
    /// The description shown for this option.
    pub description: Option<String>,
    /// The emoji displayed on this option.
    pub emoji: Option<ReactionType>,
    /// Render this option as the default selection.
    #[serde(default)]
    pub default: bool,
}

/// An input text component for modal interactions
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct InputText {
    /// The component type, it will always be [`ComponentType::InputText`].
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// An identifier defined by the developer for the select menu.
    pub custom_id: String,
    /// The input from the user
    pub value: String,
}

/// The style of the input text
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
#[repr(u8)]
pub enum InputTextStyle {
    Short = 1,
    Paragraph = 2,
    Unknown = !0,
}

enum_number!(InputTextStyle {
    Short,
    Paragraph,
    Unknown
});
<commit_msg>Allow select menu response for modals (#1986)<commit_after>use serde::de::{Deserialize, Deserializer, Error as DeError};
use serde::ser::{Serialize, Serializer};

use crate::json::{from_value, JsonMap, Value};
use crate::model::channel::ReactionType;

/// The type of a component
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
#[repr(u8)]
pub enum ComponentType {
    ActionRow = 1,
    Button = 2,
    SelectMenu = 3,
    InputText = 4,
    Unknown = !0,
}

enum_number!(ComponentType {
    ActionRow,
    Button,
    SelectMenu,
    InputText
});

/// An action row.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ActionRow {
    /// The type of component this ActionRow is.
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// The components of this ActionRow.
    #[serde(default)]
    pub components: Vec<ActionRowComponent>,
}

// A component which can be inside of an [`ActionRow`].
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ActionRowComponent {
    Button(Button),
    SelectMenu(SelectMenu),
    InputText(InputText),
}

impl<'de> Deserialize<'de> for ActionRowComponent {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let map = JsonMap::deserialize(deserializer)?;

        let kind = map
            .get("type")
            .ok_or_else(|| DeError::custom("expected type"))
            .and_then(ComponentType::deserialize)
            .map_err(DeError::custom)?;

        match kind {
            ComponentType::Button => from_value::<Button>(Value::from(map))
                .map(ActionRowComponent::Button)
                .map_err(DeError::custom),
            ComponentType::SelectMenu => from_value::<SelectMenu>(Value::from(map))
                .map(ActionRowComponent::SelectMenu)
                .map_err(DeError::custom),
            ComponentType::InputText => from_value::<InputText>(Value::from(map))
                .map(ActionRowComponent::InputText)
                .map_err(DeError::custom),
            _ => Err(DeError::custom("Unknown component type")),
        }
    }
}

impl Serialize for ActionRowComponent {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::Button(c) => Button::serialize(c, serializer),
            Self::SelectMenu(c) => SelectMenu::serialize(c, serializer),
            Self::InputText(c) => InputText::serialize(c, serializer),
        }
    }
}

impl From<Button> for ActionRowComponent {
    fn from(component: Button) -> Self {
        ActionRowComponent::Button(component)
    }
}

impl From<SelectMenu> for ActionRowComponent {
    fn from(component: SelectMenu) -> Self {
        ActionRowComponent::SelectMenu(component)
    }
}

/// A button component.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Button {
    /// The component type, it will always be [`ComponentType::Button`].
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// The button style.
    pub style: ButtonStyle,
    /// The text which appears on the button.
    pub label: Option<String>,
    /// The emoji of this button, if there is one.
    pub emoji: Option<ReactionType>,
    /// An identifier defined by the developer for the button.
    pub custom_id: Option<String>,
    /// The url of the button, if there is one.
    pub url: Option<String>,
    /// Whether the button is disabled.
    #[serde(default)]
    pub disabled: bool,
}

/// The style of a button.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
#[repr(u8)]
pub enum ButtonStyle {
    Primary = 1,
    Secondary = 2,
    Success = 3,
    Danger = 4,
    Link = 5,
    Unknown = !0,
}

enum_number!(ButtonStyle {
    Primary,
    Secondary,
    Success,
    Danger,
    Link
});

/// A select menu component.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SelectMenu {
    /// The component type, it will always be [`ComponentType::SelectMenu`].
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// The placeholder shown when nothing is selected.
    pub placeholder: Option<String>,
    /// An identifier defined by the developer for the select menu.
    pub custom_id: Option<String>,
    /// The minimum number of selections allowed.
    pub min_values: Option<u64>,
    /// The maximum number of selections allowed.
    pub max_values: Option<u64>,
    /// The options of this select menu.
    #[serde(default)]
    pub options: Vec<SelectMenuOption>,
    /// The result location for modals
    pub values: Vec<String>,
}

/// A select menu component options.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SelectMenuOption {
    /// The text displayed on this option.
    pub label: String,
    /// The value to be sent for this option.
    pub value: String,
    /// The description shown for this option.
    pub description: Option<String>,
    /// The emoji displayed on this option.
    pub emoji: Option<ReactionType>,
    /// Render this option as the default selection.
    #[serde(default)]
    pub default: bool,
}

/// An input text component for modal interactions
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct InputText {
    /// The component type, it will always be [`ComponentType::InputText`].
    #[serde(rename = "type")]
    pub kind: ComponentType,
    /// An identifier defined by the developer for the select menu.
    pub custom_id: String,
    /// The input from the user
    pub value: String,
}

/// The style of the input text
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
#[non_exhaustive]
#[repr(u8)]
pub enum InputTextStyle {
    Short = 1,
    Paragraph = 2,
    Unknown = !0,
}

enum_number!(InputTextStyle {
    Short,
    Paragraph,
    Unknown
});
<|endoftext|>


<commit_before>pub trait RequestTimeoutable {
    type Output;

    fn append_request_timeout(self, timeout: u64) -> Self::Output;
}

macro_rules! request_timeoutable {
    ($($t:ty), +) => (
        $(impl RequestTimeoutable for $t {
            type Output = $t;

            fn append_request_timeout(mut self, timeout: u64) -> Self::Output {
                let string = format!("{}", timeout);
                self.arguments.insert("request_timeout".to_string(), string.clone());
                self
            }
        })+
    )
}
<commit_msg>request_timeoutable: Use 64bit float type to represent under 1 second value<commit_after>pub trait RequestTimeoutable {
    type Output;

    /// Specify `timeout` value. Unit: second
    ///
    /// Panics if specified negative float value.
    fn append_request_timeout(self, timeout: f64) -> Self::Output;
}

macro_rules! request_timeoutable {
    ($($t:ty), +) => (
        $(impl RequestTimeoutable for $t {
            type Output = $t;

            fn append_request_timeout(mut self, timeout: f64) -> Self::Output {
                if !timeout.is_sign_positive() {
                    panic!("`timeout` should be positive 64bit float number.")
                }
                let string = format!("{}", timeout);
                self.arguments.insert("request_timeout".to_string(), string.clone());
                self
            }
        })+
    )
}
<|endoftext|>


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

//! This file handles the relationships between free regions --
//! meaning lifetime parameters. Ordinarily, free regions are
//! unrelated to one another, but they can be related via implied or
//! explicit bounds.  In that case, we track the bounds using the
//! `TransitiveRelation` type and use that to decide when one free
//! region outlives another and so forth.

use hir::def_id::DefId;
use middle::region;
use ty::{self, Lift, TyCtxt, Region};
use rustc_data_structures::transitive_relation::TransitiveRelation;

/// Combines a `region::ScopeTree` (which governs relationships between
/// scopes) and a `FreeRegionMap` (which governs relationships between
/// free regions) to yield a complete relation between concrete
/// regions.
///
/// This stuff is a bit convoluted and should be refactored, but as we
/// move to NLL it'll all go away anyhow.
pub struct RegionRelations<'a, 'gcx: 'tcx, 'tcx: 'a> {
    pub tcx: TyCtxt<'a, 'gcx, 'tcx>,

    /// context used to fetch the region maps
    pub context: DefId,

    /// region maps for the given context
    pub region_scope_tree: &'a region::ScopeTree,

    /// free-region relationships
    pub free_regions: &'a FreeRegionMap<'tcx>,
}

impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
    pub fn new(
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        context: DefId,
        region_scope_tree: &'a region::ScopeTree,
        free_regions: &'a FreeRegionMap<'tcx>,
    ) -> Self {
        Self {
            tcx,
            context,
            region_scope_tree,
            free_regions,
        }
    }

    /// Determines whether one region is a subregion of another.  This is intended to run *after
    /// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
    pub fn is_subregion_of(&self,
                           sub_region: ty::Region<'tcx>,
                           super_region: ty::Region<'tcx>)
                           -> bool {
        let result = sub_region == super_region || {
            match (sub_region, super_region) {
                (&ty::ReEmpty, _) |
                (_, &ty::ReStatic) =>
                    true,

                (&ty::ReScope(sub_scope), &ty::ReScope(super_scope)) =>
                    self.region_scope_tree.is_subscope_of(sub_scope, super_scope),

                (&ty::ReScope(sub_scope), &ty::ReEarlyBound(ref br)) => {
                    let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
                    self.region_scope_tree.is_subscope_of(sub_scope, fr_scope)
                }

                (&ty::ReScope(sub_scope), &ty::ReFree(ref fr)) => {
                    let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
                    self.region_scope_tree.is_subscope_of(sub_scope, fr_scope)
                }

                (&ty::ReEarlyBound(_), &ty::ReEarlyBound(_)) |
                (&ty::ReFree(_), &ty::ReEarlyBound(_)) |
                (&ty::ReEarlyBound(_), &ty::ReFree(_)) |
                (&ty::ReFree(_), &ty::ReFree(_)) =>
                    self.free_regions.relation.contains(&sub_region, &super_region),

                _ =>
                    false,
            }
        };
        let result = result || self.is_static(super_region);
        debug!("is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
               sub_region, super_region, result);
        result
    }

    /// Determines whether this free-region is required to be 'static
    fn is_static(&self, super_region: ty::Region<'tcx>) -> bool {
        debug!("is_static(super_region={:?})", super_region);
        match *super_region {
            ty::ReStatic => true,
            ty::ReEarlyBound(_) | ty::ReFree(_) => {
                let re_static = self.tcx.mk_region(ty::ReStatic);
                self.free_regions.relation.contains(&re_static, &super_region)
            }
            _ => false
        }
    }

    pub fn lub_free_regions(&self,
                            r_a: Region<'tcx>,
                            r_b: Region<'tcx>)
                            -> Region<'tcx> {
        self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
    }
}

#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct FreeRegionMap<'tcx> {
    // Stores the relation `a < b`, where `a` and `b` are regions.
    //
    // Invariant: only free regions like `'x` or `'static` are stored
    // in this relation, not scopes.
    relation: TransitiveRelation<Region<'tcx>>
}

impl<'tcx> FreeRegionMap<'tcx> {
    pub fn new() -> Self {
        FreeRegionMap { relation: TransitiveRelation::new() }
    }

    pub fn is_empty(&self) -> bool {
        self.relation.is_empty()
    }

    pub fn relate_free_regions_from_predicates(&mut self,
                                               predicates: &[ty::Predicate<'tcx>]) {
        debug!("relate_free_regions_from_predicates(predicates={:?})", predicates);
        for predicate in predicates {
            match *predicate {
                ty::Predicate::Projection(..) |
                ty::Predicate::Trait(..) |
                ty::Predicate::Equate(..) |
                ty::Predicate::Subtype(..) |
                ty::Predicate::WellFormed(..) |
                ty::Predicate::ObjectSafe(..) |
                ty::Predicate::ClosureKind(..) |
                ty::Predicate::TypeOutlives(..) |
                ty::Predicate::ConstEvaluatable(..) => {
                    // No region bounds here
                }
                ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => {
                    self.relate_regions(r_b, r_a);
                }
            }
        }
    }

    // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
    // (with the exception that `'static: 'x` is not notable)
    pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
        if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) {
            self.relation.add(sub, sup)
        }
    }

    pub fn lub_free_regions<'a, 'gcx>(&self,
                                      tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                      r_a: Region<'tcx>,
                                      r_b: Region<'tcx>)
                                      -> Region<'tcx> {
        assert!(is_free(r_a));
        assert!(is_free(r_b));
        let result = if r_a == r_b { r_a } else {
            match self.relation.postdom_upper_bound(&r_a, &r_b) {
                None => tcx.mk_region(ty::ReStatic),
                Some(r) => *r,
            }
        };
        debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
        result
    }
}

fn is_free(r: Region) -> bool {
    match *r {
        ty::ReEarlyBound(_) | ty::ReFree(_) => true,
        _ => false
    }
}

impl_stable_hash_for!(struct FreeRegionMap<'tcx> {
    relation
});

impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
    type Lifted = FreeRegionMap<'tcx>;
    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<FreeRegionMap<'tcx>> {
        self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx))
                     .map(|relation| FreeRegionMap { relation })
    }
}
<commit_msg>add `regions_that_outlive` to `FreeRegionMap`<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This file handles the relationships between free regions --
//! meaning lifetime parameters. Ordinarily, free regions are
//! unrelated to one another, but they can be related via implied or
//! explicit bounds.  In that case, we track the bounds using the
//! `TransitiveRelation` type and use that to decide when one free
//! region outlives another and so forth.

use hir::def_id::DefId;
use middle::region;
use ty::{self, Lift, TyCtxt, Region};
use rustc_data_structures::transitive_relation::TransitiveRelation;

/// Combines a `region::ScopeTree` (which governs relationships between
/// scopes) and a `FreeRegionMap` (which governs relationships between
/// free regions) to yield a complete relation between concrete
/// regions.
///
/// This stuff is a bit convoluted and should be refactored, but as we
/// move to NLL it'll all go away anyhow.
pub struct RegionRelations<'a, 'gcx: 'tcx, 'tcx: 'a> {
    pub tcx: TyCtxt<'a, 'gcx, 'tcx>,

    /// context used to fetch the region maps
    pub context: DefId,

    /// region maps for the given context
    pub region_scope_tree: &'a region::ScopeTree,

    /// free-region relationships
    pub free_regions: &'a FreeRegionMap<'tcx>,
}

impl<'a, 'gcx, 'tcx> RegionRelations<'a, 'gcx, 'tcx> {
    pub fn new(
        tcx: TyCtxt<'a, 'gcx, 'tcx>,
        context: DefId,
        region_scope_tree: &'a region::ScopeTree,
        free_regions: &'a FreeRegionMap<'tcx>,
    ) -> Self {
        Self {
            tcx,
            context,
            region_scope_tree,
            free_regions,
        }
    }

    /// Determines whether one region is a subregion of another.  This is intended to run *after
    /// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
    pub fn is_subregion_of(&self,
                           sub_region: ty::Region<'tcx>,
                           super_region: ty::Region<'tcx>)
                           -> bool {
        let result = sub_region == super_region || {
            match (sub_region, super_region) {
                (&ty::ReEmpty, _) |
                (_, &ty::ReStatic) =>
                    true,

                (&ty::ReScope(sub_scope), &ty::ReScope(super_scope)) =>
                    self.region_scope_tree.is_subscope_of(sub_scope, super_scope),

                (&ty::ReScope(sub_scope), &ty::ReEarlyBound(ref br)) => {
                    let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
                    self.region_scope_tree.is_subscope_of(sub_scope, fr_scope)
                }

                (&ty::ReScope(sub_scope), &ty::ReFree(ref fr)) => {
                    let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
                    self.region_scope_tree.is_subscope_of(sub_scope, fr_scope)
                }

                (&ty::ReEarlyBound(_), &ty::ReEarlyBound(_)) |
                (&ty::ReFree(_), &ty::ReEarlyBound(_)) |
                (&ty::ReEarlyBound(_), &ty::ReFree(_)) |
                (&ty::ReFree(_), &ty::ReFree(_)) =>
                    self.free_regions.relation.contains(&sub_region, &super_region),

                _ =>
                    false,
            }
        };
        let result = result || self.is_static(super_region);
        debug!("is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
               sub_region, super_region, result);
        result
    }

    /// Determines whether this free-region is required to be 'static
    fn is_static(&self, super_region: ty::Region<'tcx>) -> bool {
        debug!("is_static(super_region={:?})", super_region);
        match *super_region {
            ty::ReStatic => true,
            ty::ReEarlyBound(_) | ty::ReFree(_) => {
                let re_static = self.tcx.mk_region(ty::ReStatic);
                self.free_regions.relation.contains(&re_static, &super_region)
            }
            _ => false
        }
    }

    pub fn lub_free_regions(&self,
                            r_a: Region<'tcx>,
                            r_b: Region<'tcx>)
                            -> Region<'tcx> {
        self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
    }
}

#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct FreeRegionMap<'tcx> {
    // Stores the relation `a < b`, where `a` and `b` are regions.
    //
    // Invariant: only free regions like `'x` or `'static` are stored
    // in this relation, not scopes.
    relation: TransitiveRelation<Region<'tcx>>
}

impl<'tcx> FreeRegionMap<'tcx> {
    pub fn new() -> Self {
        FreeRegionMap { relation: TransitiveRelation::new() }
    }

    pub fn is_empty(&self) -> bool {
        self.relation.is_empty()
    }

    pub fn relate_free_regions_from_predicates(&mut self,
                                               predicates: &[ty::Predicate<'tcx>]) {
        debug!("relate_free_regions_from_predicates(predicates={:?})", predicates);
        for predicate in predicates {
            match *predicate {
                ty::Predicate::Projection(..) |
                ty::Predicate::Trait(..) |
                ty::Predicate::Equate(..) |
                ty::Predicate::Subtype(..) |
                ty::Predicate::WellFormed(..) |
                ty::Predicate::ObjectSafe(..) |
                ty::Predicate::ClosureKind(..) |
                ty::Predicate::TypeOutlives(..) |
                ty::Predicate::ConstEvaluatable(..) => {
                    // No region bounds here
                }
                ty::Predicate::RegionOutlives(ty::Binder(ty::OutlivesPredicate(r_a, r_b))) => {
                    self.relate_regions(r_b, r_a);
                }
            }
        }
    }

    // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`.
    // (with the exception that `'static: 'x` is not notable)
    pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
        if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) {
            self.relation.add(sub, sup)
        }
    }

    pub fn lub_free_regions<'a, 'gcx>(&self,
                                      tcx: TyCtxt<'a, 'gcx, 'tcx>,
                                      r_a: Region<'tcx>,
                                      r_b: Region<'tcx>)
                                      -> Region<'tcx> {
        assert!(is_free(r_a));
        assert!(is_free(r_b));
        let result = if r_a == r_b { r_a } else {
            match self.relation.postdom_upper_bound(&r_a, &r_b) {
                None => tcx.mk_region(ty::ReStatic),
                Some(r) => *r,
            }
        };
        debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result);
        result
    }

    /// Returns all regions that are known to outlive `r_a`. For
    /// example, in a function:
    ///
    /// ```
    /// fn foo<'a, 'b: 'a, 'c: 'b>() { .. }
    /// ```
    ///
    /// if `r_a` represents `'a`, this function would return `{'b, 'c}`.
    pub fn regions_that_outlive<'a, 'gcx>(&self, r_a: Region<'tcx>) -> Vec<&Region<'tcx>> {
        assert!(is_free(r_a));
        self.relation.greater_than(&r_a)
    }
}

fn is_free(r: Region) -> bool {
    match *r {
        ty::ReEarlyBound(_) | ty::ReFree(_) => true,
        _ => false
    }
}

impl_stable_hash_for!(struct FreeRegionMap<'tcx> {
    relation
});

impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
    type Lifted = FreeRegionMap<'tcx>;
    fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<FreeRegionMap<'tcx>> {
        self.relation.maybe_map(|&fr| fr.lift_to_tcx(tcx))
                     .map(|relation| FreeRegionMap { relation })
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use parser::ast::*;
use scope::context::*;
use processing::structs::*;

type FormalProp<'a> = (&'a str);
type FormalPropVec<'a> = Vec<FormalProp<'a>>;

type PropKeyRef = (String, String);
type PropKeyRefVec = Vec<PropKeyRef>;

type PropValue<'a> = (&'a str, Option<&'a ExprValue>);
type PropValueVec<'a> = Vec<PropValue<'a>>;

#[derive(Debug, Clone, Default)]
pub struct CompDefProcessorOutput {
    ty: Option<String>,
    ops: Option<OpsVec>
}

impl Into<Component> for CompDefProcessorOutput {
    fn into(self) -> Component {
        Component::new_with_vec(self.ty.unwrap(), self.ops)
    }
}

#[derive(Debug)]
pub struct CompDefProcessor<'out> {
    output: &'out mut CompDefProcessorOutput
}

impl<'out> CompDefProcessor<'out> {
    pub fn with_output(output: &'out mut CompDefProcessorOutput) -> Self {
        CompDefProcessor { output: output }
    }

    fn add_op(&mut self, op: ElementOp) {
        if let Some(ref mut ops) = self.output.ops {
            ops.push(op);
        } else {
            self.output.ops = Some(vec![op]);
        }
    }

    pub fn push_component_definition_scope<'a, I>(&mut self, ctx: &mut Context,component_ty: &str, formals: I)
        where I: IntoIterator<Item = &'a FormalProp<'a>>
    {
        if self.output.ty.is_some() {
            panic!("Already have component defintion in this output.");
        }
        self.output.ty = Some(component_ty.to_owned());

        ctx.push_child_scope();
        for formal in formals {
            ctx.add_unbound_formal_param(formal);
        }
    }

    pub fn push_element_parameter_definition_scope<'a, I>(&mut self, ctx: &mut Context, element_id: &str, element_ty: &str, props: Option<I>)
        where I: IntoIterator<Item = &'a PropKeyRef> + Clone
    {
        // let props = props.map(|props| props.clone().into_iter());
        let element_props = props.as_ref().map(|props| props.clone().into_iter().map(|prop| {
            let key = &prop.0;
            let ref_key = &prop.1;
            let value_sym = ctx.resolve_sym(ref_key);
            if let Some(value_sym) = value_sym {
                return (key.to_owned(), Some(ExprValue::SymbolReference(value_sym)));
            };
            (key.to_owned(), None)
        }).collect());

        let op = ElementOp::ElementVoid(element_ty.to_owned(), Some(element_id.to_owned()), element_props, None, None);
        self.add_op(op);

        let parent_scope_id = ctx.scope().id().to_owned();
        ctx.push_child_scope();
        if let Some(props) = props {
            for prop in props {
                ctx.add_element_prop_ref(&prop.0, &prop.1, Some(&parent_scope_id));
            }
        }
    }

    pub fn push_element_scope(&mut self, ctx: &mut Context, element_id: &str, _element_ty: &str) {
        ctx.push_child_scope();
        ctx.append_path_str(element_id);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::iter::*;
    use parser::ast::*;
    use scope::context::*;

    trait OpsWriter {
        fn write_op(&mut self, op: &ElementOp);
    }

    #[derive(Debug, Default)]
    struct TestOpsWriter {
    }

    impl OpsWriter for TestOpsWriter {
        fn write_op(&mut self, op: &ElementOp) {

        }
    }

    #[derive(Debug, Default)]
    struct ComponentWriter {
        ctx: Context
    }

    impl ComponentWriter {
        fn resolve_props<I>(&mut self, props: I) -> impl IntoIterator<Item = PropValue>
          where I: IntoIterator<Item = Prop>
        {
            props.map(|prop| {
                let resolved_sym = self.ctx.resolve_sym(&prop.0);
                if let Some(resolved_sym) = resolved_sym {
                    let key = &prop.0.to_owned();
                    let expr = ExprValue::SymbolReference(resolved_sym);
                    return (key, Some(expr));
                }

                return prop.clone();
            })
        }

        fn write_op_to<W>(&mut self, ops_writer: &mut W, comp_op: &ElementOp)
          where W: OpsWriter
        {
            match comp_op {
                &ElementOp::ElementVoid(ref element_ty, ref element_id, ref props, ref event_handlers, ref value_bindings) => {
                    let resolved_props = props.map(|props| self.resolve_props(props));
                    let op = ElementOp::ElementVoid(element_ty.to_owned(), element_id.to_owned(), resolved_props, event_handlers.to_owned(), value_bindings.to_owned());
                    ops_writer.write_op(&op);
                },
                _ => { ops_writer.write_op(comp_op); }
            };
        }

        pub fn write_component<W>(&mut self, ops_writer: &mut W, comp: &Component)
          where W: OpsWriter
        {
            if let Some(ops_iter) = comp.ops_iter() {
                for op in ops_iter {
                    ops_writer.write_op(op);
                }
            };
        }
    }

    #[test]
    pub fn test_process_comp_def1() {
        let mut output = CompDefProcessorOutput::default();

        let comp_definition_scope_id: String;
        // let element_param_defs_scope_id: String;
        {
            let mut ctx = Context::default();
            let mut processor = CompDefProcessor::with_output(&mut output);

            // component Component(todo)
            // Create new component context with unbound formal prop (todo)
            {
                let formals: FormalPropVec = vec![("todo")];
                processor.push_component_definition_scope(&mut ctx, "Component", formals.iter());
            }
            comp_definition_scope_id = ctx.scope().id().to_owned();

            // within Component definition
            {
                // The local (todo) should be an unbound formal prop (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    // Some(Symbol::ref_prop_in_scope("todo", "todo", Some(&lm_element_scope_id)))
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
            // element_param_defs_scope_id = ctx.scope().id().to_owned();

            // element Pq invocation
            {
                let props: PropKeyRefVec = vec![
                    ("value".into(), "todo".into())
                ];
                processor.push_element_parameter_definition_scope(&mut ctx, "Pq", "input", Some(props.iter()));
            }
            // let element_pq_invocation_scope_id = ctx.scope().id().to_owned();

            // element Pq scope
            {
                processor.push_element_scope(&mut ctx, "Pq", "input");

                // The local (todo) should still be an unbound formal param (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
        }

        // Verify output
        let comp: Component = output.into();
        assert_eq!(comp.ty(), "Component");
        assert_eq!(
            comp.ops_iter().map(|v| v.into_iter().cloned().collect()),
            Some(vec![
                ElementOp::ElementVoid("input".into(), Some("Pq".into()), Some(vec![(
                    "value".into(),
                    Some(ExprValue::SymbolReference(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)) ))
                )]), None, None)
            ])
        );

        // Test writer
        let mut ops_writer = TestOpsWriter::default();
        let mut comp_writer = ComponentWriter::default();

        comp_writer.write_component(&mut ops_writer, &comp);
    }
}<commit_msg>Implement SymbolResolver in process_comp_def test<commit_after>#![allow(dead_code)]

use parser::ast::*;
use scope::context::*;
use processing::structs::*;

type FormalProp<'a> = (&'a str);
type FormalPropVec<'a> = Vec<FormalProp<'a>>;

type PropKeyRef = (String, String);
type PropKeyRefVec = Vec<PropKeyRef>;

type PropValue<'a> = (&'a str, Option<&'a ExprValue>);
type PropValueVec<'a> = Vec<PropValue<'a>>;

#[derive(Debug, Clone, Default)]
pub struct CompDefProcessorOutput {
    ty: Option<String>,
    ops: Option<OpsVec>
}

impl Into<Component> for CompDefProcessorOutput {
    fn into(self) -> Component {
        Component::new_with_vec(self.ty.unwrap(), self.ops)
    }
}

#[derive(Debug)]
pub struct CompDefProcessor<'out> {
    output: &'out mut CompDefProcessorOutput
}

impl<'out> CompDefProcessor<'out> {
    pub fn with_output(output: &'out mut CompDefProcessorOutput) -> Self {
        CompDefProcessor { output: output }
    }

    fn add_op(&mut self, op: ElementOp) {
        if let Some(ref mut ops) = self.output.ops {
            ops.push(op);
        } else {
            self.output.ops = Some(vec![op]);
        }
    }

    pub fn push_component_definition_scope<'a, I>(&mut self, ctx: &mut Context,component_ty: &str, formals: I)
        where I: IntoIterator<Item = &'a FormalProp<'a>>
    {
        if self.output.ty.is_some() {
            panic!("Already have component defintion in this output.");
        }
        self.output.ty = Some(component_ty.to_owned());

        ctx.push_child_scope();
        for formal in formals {
            ctx.add_unbound_formal_param(formal);
        }
    }

    pub fn push_element_parameter_definition_scope<'a, I>(&mut self, ctx: &mut Context, element_id: &str, element_ty: &str, props: Option<I>)
        where I: IntoIterator<Item = &'a PropKeyRef> + Clone
    {
        // let props = props.map(|props| props.clone().into_iter());
        let element_props = props.as_ref().map(|props| props.clone().into_iter().map(|prop| {
            let key = &prop.0;
            let ref_key = &prop.1;
            let value_sym = ctx.resolve_sym(ref_key);
            if let Some(value_sym) = value_sym {
                return (key.to_owned(), Some(ExprValue::SymbolReference(value_sym)));
            };
            (key.to_owned(), None)
        }).collect());

        let op = ElementOp::ElementVoid(element_ty.to_owned(), Some(element_id.to_owned()), element_props, None, None);
        self.add_op(op);

        let parent_scope_id = ctx.scope().id().to_owned();
        ctx.push_child_scope();
        if let Some(props) = props {
            for prop in props {
                ctx.add_element_prop_ref(&prop.0, &prop.1, Some(&parent_scope_id));
            }
        }
    }

    pub fn push_element_scope(&mut self, ctx: &mut Context, element_id: &str, _element_ty: &str) {
        ctx.push_child_scope();
        ctx.append_path_str(element_id);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::iter::*;
    use parser::ast::*;
    use scope::context::*;

    trait OpsWriter {
        fn write_op(&mut self, op: &ElementOp);
    }

    #[derive(Debug, Default)]
    struct TestOpsWriter {
    }

    impl OpsWriter for TestOpsWriter {
        fn write_op(&mut self, op: &ElementOp) {

        }
    }

    #[derive(Debug, Default)]
    struct ComponentWriter {
        ctx: Context
    }

    type PropValue<'a> = (&'a str, Option<&'a ExprValue>);

    #[derive(Debug)]
    struct SymbolResolver<'a, I: Iterator<Item = &'a str>> {
        ctx: &'a mut Context,
        iter: I
    }

    // impl<'a, I> SymbolResolver<'a, I>
    // //   where I: Iterator<Item = &'a PropValue<'a>>
    // {
    //     pub fn new(ctx: &'a mut Context, iter: Self::I) -> Self {
    //         SymbolResolver { ctx: ctx, iter: iter }
    //     }
    // }

    // impl<'a, I> Iterator for SymbolResolver<'a, I>
    //   where I: Iterator<Item = &'a PropValue<'a>>
    impl<'a, I: Iterator<Item = &'a str>> Iterator for SymbolResolver<'a, I>
    {
        type Item = Prop;

        fn next(&mut self) -> Option<Self::Item> {
            if let Some(key) = self.iter.next() {
                let resolved_sym = self.ctx.resolve_sym(key);
                if let Some(resolved_sym) = resolved_sym {
                    // let key = prop.0.to_owned();
                    let expr = ExprValue::SymbolReference(resolved_sym);
                    return Some((key.to_owned(), Some(expr)));
                };
                return Some((key.to_owned(), None));
                // return Some((key.to_owned(), prop.1.map(|p| p.to_owned())));
            };
            None

            // self.iter.next().map(|prop| {
            //     let resolved_sym = self.ctx.resolve_sym(&prop.0);
            //     if let Some(resolved_sym) = resolved_sym {
            //         let key = prop.0.to_owned();
            //         let expr = ExprValue::SymbolReference(resolved_sym);
            //         return (key, Some(expr));
            //     }

            //     return (prop.0.to_owned(), prop.1.map(|p| p.to_owned()))
            // })
        }
    }

    // impl<B, I: ExactSizeIterator, F> ExactSizeIterator for SymbolResolver<I>
    // {
    //     fn len(&self) -> usize {
    //         self.iter.len()
    //     }

    //     fn is_empty(&self) -> bool {
    //         self.iter.is_empty()
    //     }
    // }

    impl<'a> ComponentWriter {
        // fn resolve_props<'props, I>(&'a mut self, props: I) -> SymbolResolver<'a, I>
        //   where I: Iterator<Item = PropValue<'props>>
        // {
        //     SymbolResolver::new(&mut self.ctx, props)
        // }

        fn write_op_to<W>(&mut self, ops_writer: &mut W, comp_op: &ElementOp)
          where W: OpsWriter
        {
            match comp_op {
                &ElementOp::ElementVoid(ref element_ty, ref element_id, ref props, ref event_handlers, ref value_bindings) => {
                    if let &Some(ref props) = props {
                        // let v: Vec<PropValue<'a>> = vec![];
                        // let v: PropVec = vec![];
                        // let mut prop_iter = v.iter().map(|prop| (prop.0.as_ref(), prop.1.as_ref()));

                        let mut prop_iter = props.iter().map(|prop| prop.0.as_str());
                        let mut iter = SymbolResolver { ctx: &mut self.ctx, iter: prop_iter };
                        let resolved_props: Vec<Prop> = iter.collect();

                        let op = ElementOp::ElementVoid(element_ty.to_owned(), element_id.to_owned(), Some(resolved_props), event_handlers.to_owned(), value_bindings.to_owned());
                        ops_writer.write_op(&op);
                    };

                    // let resolved_props = props.map(|props| {
                    //     let mut props_iter = props.iter().map(|prop| (prop.0.as_ref(), prop.1.map(|p| &p)));
                    //     let mut iter = SymbolResolver { ctx: &mut self.ctx, iter: props_iter };

                    //     // let mut iter = self.resolve_props(
                    //     //     props.iter().map(|prop| (prop.0.as_ref(), prop.1.map(|p| &p)))
                    //     // );
                    //     let resolved_prop = iter.next().unwrap();
                    //     // let resolved_props: PropVec = iter.collect();

                    //     // resolved_props
                    //     vec![]
                    // });
                    // let op = ElementOp::ElementVoid(element_ty.to_owned(), element_id.to_owned(), resolved_props, event_handlers.to_owned(), value_bindings.to_owned());
                    // ops_writer.write_op(&op);
                },
                _ => { ops_writer.write_op(comp_op); }
            };
        }

        pub fn write_component<W>(&mut self, ops_writer: &mut W, comp: &Component)
          where W: OpsWriter
        {
            if let Some(ops_iter) = comp.ops_iter() {
                for op in ops_iter {
                    ops_writer.write_op(op);
                }
            };
        }
    }

    #[test]
    pub fn test_process_comp_def1() {
        let mut output = CompDefProcessorOutput::default();

        let comp_definition_scope_id: String;
        // let element_param_defs_scope_id: String;
        {
            let mut ctx = Context::default();
            let mut processor = CompDefProcessor::with_output(&mut output);

            // component Component(todo)
            // Create new component context with unbound formal prop (todo)
            {
                let formals: FormalPropVec = vec![("todo")];
                processor.push_component_definition_scope(&mut ctx, "Component", formals.iter());
            }
            comp_definition_scope_id = ctx.scope().id().to_owned();

            // within Component definition
            {
                // The local (todo) should be an unbound formal prop (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    // Some(Symbol::ref_prop_in_scope("todo", "todo", Some(&lm_element_scope_id)))
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
            // element_param_defs_scope_id = ctx.scope().id().to_owned();

            // element Pq invocation
            {
                let props: PropKeyRefVec = vec![
                    ("value".into(), "todo".into())
                ];
                processor.push_element_parameter_definition_scope(&mut ctx, "Pq", "input", Some(props.iter()));
            }
            // let element_pq_invocation_scope_id = ctx.scope().id().to_owned();

            // element Pq scope
            {
                processor.push_element_scope(&mut ctx, "Pq", "input");

                // The local (todo) should still be an unbound formal param (todo)
                assert_eq!(ctx.resolve_sym("todo"),
                    Some(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)))
                );
            }
        }

        // Verify output
        let comp: Component = output.into();
        assert_eq!(comp.ty(), "Component");
        assert_eq!(
            comp.ops_iter().map(|v| v.into_iter().cloned().collect()),
            Some(vec![
                ElementOp::ElementVoid("input".into(), Some("Pq".into()), Some(vec![(
                    "value".into(),
                    Some(ExprValue::SymbolReference(Symbol::unbound_formal_param("todo", Some(&comp_definition_scope_id)) ))
                )]), None, None)
            ])
        );

        // Test writer
        let mut ops_writer = TestOpsWriter::default();
        let mut comp_writer = ComponentWriter::default();

        comp_writer.write_component(&mut ops_writer, &comp);
    }
}<|endoftext|>