<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use crate::action;
use crate::session;

pub const STARTUP: Sink = Sink { id: "/flows/F:Startup" };

pub struct Sink {
    id: &'static str,
}

impl Sink {

    pub fn wrap<R>(&self, response: R) -> session::Response<R>
    where
        R: action::Response,
    {
        session::Response {
            session_id: String::from(self.id),
            request_id: None,
            response_id: None,
            data: response,
        }
    }
}
<commit_msg>Derive useful instances for the sink identifier.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use crate::action;
use crate::session;

pub const STARTUP: Sink = Sink { id: "/flows/F:Startup" };

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Sink {
    id: &'static str,
}

impl Sink {

    pub fn wrap<R>(&self, response: R) -> session::Response<R>
    where
        R: action::Response,
    {
        session::Response {
            session_id: String::from(self.id),
            request_id: None,
            response_id: None,
            data: response,
        }
    }
}
<|endoftext|>


<commit_before>use super::command;
use super::chain;

pub fn create_droplet_by_name(name: &str) {

    let ssh_key_mapping_func = |res: &command::Result, cmd_str: String| -> String {
        let ids : Vec<&str> = res.stdout.lines().collect();
        let new_cmd = str::replace(&cmd_str, "%ssh_keys%", &ids.join(","));
        new_cmd.to_string()
    };

    let ip_address_mapping_func = |res: &command::Result, cmd_str: String| -> String {
        let mut ip_address : Option<String> = None;
        let res_stdout = res.stdout.clone();
        for line in res_stdout.lines() {
            if line.starts_with(name) {
                debug!("Found: {}", line);
                let fields : Vec<&str> = line.split_whitespace().collect();
                if fields.len() < 2 {
                    warn!("Couldn't find ip address in line: {}", line);
                }
                ip_address = Some(fields[1].to_string());
                break;
            }
        }
        if ip_address == None {
            error!("Couldn't locate droplet in output: {}", res_stdout);
            return "--will fail--".to_string()
        }

        let new_cmd = str::replace(&cmd_str, "%ip_address%", &ip_address.unwrap());
        new_cmd.to_string()
    };

    let create_str = format!("doctl compute droplet create {} --image=ubuntu-16-04-x64 --region=sfo1 --size=512mb --ssh-keys=\"%ssh_keys%\" --wait", name);
    let record_str = format!("doctl compute domain records create one.haus --record-type=A --record-data=%ip_address% --record-name={}", name);

    // TODO: check the result heh
    let _ = chain::CommandChain::new()
        .cmd("doctl compute ssh-key list --no-header --format=ID")
        .result_mapped_cmd(&ssh_key_mapping_func, &create_str)
        .cmd("doctl compute droplet list --format Name,PublicIPv4,PublicIPv6,Status")
        .result_mapped_cmd(&ip_address_mapping_func, &record_str)
        .execute();
}

pub fn destroy_droplet_by_name(name: &str) {
    let record_id_extractor = |res: &command::Result, cmd_str: String| -> String {
        let mut record_id : Option<String> = None;
        let res_stdout = res.stdout.clone();
        for line in res_stdout.lines() {
            if line.starts_with(name) {
                debug!("Found: {}", line);
                let fields : Vec<&str> = line.split_whitespace().collect();
                if fields.len() < 2 {
                    warn!("Couldn't find ip address in line: {}", line);
                }
                record_id = Some(fields[1].to_string());
                break;
            }
        }
        if record_id == None {
            error!("Couldn't locate droplet in output: {}", res_stdout);
            return "--will fail--".to_string()
        }

        let new_cmd = str::replace(&cmd_str, "%record_id%%", &record_id.unwrap());
        new_cmd.to_string()
    };

    let create_str = format!("doctl compute droplet delete -f {}", name);
    let delete_record_cmd = format!("doctl compute domain records delete -f one.haus %record_id%");

    // TODO: check the result heh
    let _ = chain::CommandChain::new()
        .cmd(&create_str)
        .cmd("doctl compute domain records list one.haus --format Name,ID --no-header")
        .result_mapped_cmd(&record_id_extractor, &delete_record_cmd)
        .execute();
}

pub fn create_sshkey(name: &str) {
    // By default, always attempt to add a new key with [name] mapping to ~/.ssh/id_rsa.pub
    let create_key_str = format!("doctl compute ssh-key create {} --public-key=\"$(cat ~/.ssh/id_rsa.pub)\"", name);
    println!("Running command:\n\t\t{}", create_key_str);
    // Create the actual droplet
    let result = command::run_host_cmd(&create_key_str);
    if !result.success {
        println!("Failed with stderr:\n\n{}", result.stderr);
    }
}
<commit_msg>fixed minor bugs in destroy droplet port<commit_after>use super::command;
use super::chain;

pub fn create_droplet_by_name(name: &str) {

    let ssh_key_mapping_func = |res: &command::Result, cmd_str: String| -> String {
        let ids : Vec<&str> = res.stdout.lines().collect();
        let new_cmd = str::replace(&cmd_str, "%ssh_keys%", &ids.join(","));
        new_cmd.to_string()
    };

    let ip_address_mapping_func = |res: &command::Result, cmd_str: String| -> String {
        let mut ip_address : Option<String> = None;
        let res_stdout = res.stdout.clone();
        for line in res_stdout.lines() {
            if line.starts_with(name) {
                debug!("Found: {}", line);
                let fields : Vec<&str> = line.split_whitespace().collect();
                if fields.len() < 2 {
                    warn!("Couldn't find ip address in line: {}", line);
                }
                ip_address = Some(fields[1].to_string());
                break;
            }
        }
        if ip_address == None {
            error!("Couldn't locate droplet in output: {}", res_stdout);
            return "--will fail--".to_string()
        }

        let new_cmd = str::replace(&cmd_str, "%ip_address%", &ip_address.unwrap());
        new_cmd.to_string()
    };

    let create_str = format!("doctl compute droplet create {} --image=ubuntu-16-04-x64 --region=sfo1 --size=512mb --ssh-keys=\"%ssh_keys%\" --wait", name);
    let record_str = format!("doctl compute domain records create one.haus --record-type=A --record-data=%ip_address% --record-name={}", name);

    // TODO: check the result heh
    let _ = chain::CommandChain::new()
        .cmd("doctl compute ssh-key list --no-header --format=ID")
        .result_mapped_cmd(&ssh_key_mapping_func, &create_str)
        .cmd("doctl compute droplet list --format Name,PublicIPv4,PublicIPv6,Status")
        .result_mapped_cmd(&ip_address_mapping_func, &record_str)
        .execute();
}

pub fn destroy_droplet_by_name(name: &str) {
    let record_id_extractor = |res: &command::Result, cmd_str: String| -> String {
        let mut record_id : Option<String> = None;
        let res_stdout = res.stdout.clone();
        for line in res_stdout.lines() {
            if line.starts_with(name) {
                debug!("Found: {}", line);
                let fields : Vec<&str> = line.split_whitespace().collect();
                if fields.len() < 2 {
                    warn!("Couldn't find ip address in line: {}", line);
                }
                record_id = Some(fields[1].to_string());
                break;
            }
        }
        if record_id == None {
            error!("Couldn't locate droplet in output: {}", res_stdout);
            return "--will fail--".to_string()
        }

        let new_cmd = str::replace(&cmd_str, "%record_id%", &record_id.unwrap());
        new_cmd.to_string()
    };

    let delete_droplet_cmd = format!("doctl compute droplet delete -f {}", name);
    let delete_record_cmd = format!("doctl compute domain records delete -f one.haus %record_id%");

    // TODO: check the result heh
    let _ = chain::CommandChain::new()
        .cmd(&delete_droplet_cmd)
        .cmd("doctl compute domain records list one.haus --format Name,ID --no-header")
        .result_mapped_cmd(&record_id_extractor, &delete_record_cmd)
        .execute();
}

pub fn create_sshkey(name: &str) {
    // By default, always attempt to add a new key with [name] mapping to ~/.ssh/id_rsa.pub
    let create_key_str = format!("doctl compute ssh-key create {} --public-key=\"$(cat ~/.ssh/id_rsa.pub)\"", name);
    println!("Running command:\n\t\t{}", create_key_str);
    // Create the actual droplet
    let result = command::run_host_cmd(&create_key_str);
    if !result.success {
        println!("Failed with stderr:\n\n{}", result.stderr);
    }
}
<|endoftext|>


<commit_before>use {time, node};
use hash::SubotaiHash;
use std::collections::HashMap;
use std::sync::RwLock;
use std::cmp;
use std::mem;

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StorageEntry {
   Value(SubotaiHash),
   Blob(Vec<u8>),
}

type KeyGroup = Vec<(time::Tm, StorageEntry)>; // expiration/entry pair

pub struct Storage {
   key_groups    : RwLock<HashMap<SubotaiHash, KeyGroup> >,
   parent_id     : SubotaiHash,
   configuration : node::Configuration,
   size_bytes    : usize,
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StoreResult {
   Success,
   StorageFull,
   BlobTooBig,
}

impl Storage {
   pub fn new(parent_id: SubotaiHash, configuration: node::Configuration) -> Storage {
      Storage {
         key_groups    : RwLock::new(HashMap::with_capacity(configuration.max_storage)),
         parent_id     : parent_id,
         configuration : configuration,
         size_bytes    : 0,
      }
   }

   /// Returns size in bytes.
   pub fn len(&self) -> usize {
      self.size_bytes
   }

   pub fn is_empty(&self) -> bool {
      self.size_bytes == 0
   }

   /// Retrieves all entries in a key_group.
   pub fn retrieve(&self, key: &SubotaiHash) -> Option<Vec<StorageEntry>> {
      if let Some(key_group) = self.key_groups.read().unwrap().get(key) {
         Some(key_group.iter().cloned().map(|pair| pair.1).collect())
      } else {
         None
      }
   }

   /// Stores an entry in a key_group, with an expiration date, if it wasn't present already.
   /// If it was present, it keeps the latest expiration time.
   pub fn store(&self, key: &SubotaiHash, entry: &StorageEntry, expiration: &time::Tm) -> StoreResult {
      // Expiration time is clamped to a reasonable value.
      let expiration = cmp::min(*expiration, time::now() + time::Duration::hours(self.configuration.base_expiration_time_hrs));
      let mut key_groups = self.key_groups.write().unwrap();
      let compound_length = Storage::compound_length(entry);

      if key_groups.contains_key(key) {
         let key_group = key_groups.get_mut(key).unwrap();
         let already_existed = if let Some(preexisting_pair) = key_group.iter_mut().find(|stored_pair| stored_pair.1 == *entry) {
            preexisting_pair.0 = expiration.clone();
            true
         } else {
            false
         };
         if !already_existed {
            if compound_length + self.len() > self.configuration.max_storage {
               return StoreResult::StorageFull;
            }
            key_group.push((expiration.clone(), entry.clone()));
            self.size_bytes += compound_length;
         }
      } else {
         if compound_length + self.len() > self.configuration.max_storage {
            return StoreResult::StorageFull;
         }
         let mut key_group = KeyGroup::new();
         key_group.push((expiration.clone(), entry.clone()));
         key_groups.insert(key.clone(), key_group);
         self.size_bytes += compound_length;
      }
      StoreResult::Success
   }

   fn compound_length(entry: &StorageEntry) -> usize {
      mem::size_of::<time::Tm>() + match entry {
         StorageEntry::Value(_) => mem::size_of::<SubotaiHash>(),
         StorageEntry::Blob(blob) => blob.len(),
      }
   }

   //pub fn clear_expired_entries(&self) {
   //   let now = time::now();
   //   let mut extended_entries = self.extended_entries.write().unwrap();
   //   let keys_to_remove: Vec<_>= extended_entries
   //      .iter()
   //      .filter_map(|(key, &ExtendedEntry{ expiration, .. })| if expiration < now { Some(key) } else { None })
   //      .cloned()
   //      .collect();

   //   for key in keys_to_remove {
   //      extended_entries.remove(&key);
   //   }
   //}

   ///// Marks all key-entry pairs as ready for republishing.
   //pub fn mark_all_as_ready(&self) {
   //   for (_, &mut ExtendedEntry {ref mut republish_ready, ..})  in self.extended_entries.write().unwrap().iter_mut() {
   //      *republish_ready = true;
   //   }
   //}

   ///// Marks a particular key-entry pair as not ready for republishing.
   //pub fn mark_as_not_ready(&self, key: &SubotaiHash) {
   //   if let Some( &mut ExtendedEntry {ref mut republish_ready, ..}) = self.extended_entries.write().unwrap().get_mut(key) {
   //      *republish_ready = false;
   //   }
   //}

   ///// Retrieves all key-entry pairs ready for republishing, together with their current expiration date
   //pub fn get_all_ready_entries(&self) -> Vec<(SubotaiHash, StorageEntry, time::Tm)>  {
   //   self.extended_entries.read().unwrap().iter()
   //      .filter(|&(_, &ExtendedEntry{ republish_ready, ..})| republish_ready)
   //      .map(|(key, &ExtendedEntry{ ref entry, ref expiration, ..})| (key.clone(), entry.clone(), expiration.clone()))
   //      .collect()
   //}
}

#[cfg(test)]
mod tests {
   use super::*;
   use {hash, time, node};
}
<commit_msg>Store<commit_after>use {time, node};
use hash::SubotaiHash;
use std::collections::HashMap;
use std::sync::RwLock;
use std::cmp;
use std::mem;

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StorageEntry {
   Value(SubotaiHash),
   Blob(Vec<u8>),
}

type KeyGroup = Vec<(time::Tm, StorageEntry)>; // expiration/entry pair

pub struct Storage {
   key_groups    : RwLock<HashMap<SubotaiHash, KeyGroup> >,
   parent_id     : SubotaiHash,
   configuration : node::Configuration,
   size_bytes    : RwLock<usize>,
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum StoreResult {
   Success,
   StorageFull,
   BlobTooBig,
}

impl Storage {
   pub fn new(parent_id: SubotaiHash, configuration: node::Configuration) -> Storage {
      Storage {
         key_groups    : RwLock::new(HashMap::with_capacity(configuration.max_storage)),
         parent_id     : parent_id,
         configuration : configuration,
         size_bytes    : RwLock::new(0),
      }
   }

   /// Returns size in bytes.
   pub fn len(&self) -> usize {
      *self.size_bytes.read().unwrap()
   }

   pub fn is_empty(&self) -> bool {
      *self.size_bytes.read().unwrap() == 0
   }

   /// Retrieves all entries in a key_group.
   pub fn retrieve(&self, key: &SubotaiHash) -> Option<Vec<StorageEntry>> {
      if let Some(key_group) = self.key_groups.read().unwrap().get(key) {
         Some(key_group.iter().cloned().map(|pair| pair.1).collect())
      } else {
         None
      }
   }

   /// Stores an entry in a key_group, with an expiration date, if it wasn't present already.
   /// If it was present, it keeps the latest expiration time.
   pub fn store(&self, key: &SubotaiHash, entry: &StorageEntry, expiration: &time::Tm) -> StoreResult {
      // Expiration time is clamped to a reasonable value.
      let expiration = cmp::min(*expiration, time::now() + time::Duration::hours(self.configuration.base_expiration_time_hrs));
      let mut key_groups = self.key_groups.write().unwrap();
      let compound_length = Storage::compound_length(entry);

      if key_groups.contains_key(key) {
         let key_group = key_groups.get_mut(key).unwrap();
         let already_existed = if let Some(preexisting_pair) = key_group.iter_mut().find(|stored_pair| stored_pair.1 == *entry) {
            preexisting_pair.0 = expiration.clone();
            true
         } else {
            false
         };
         if !already_existed {
            if compound_length + self.len() > self.configuration.max_storage {
               return StoreResult::StorageFull;
            }
            key_group.push((expiration.clone(), entry.clone()));
            *self.size_bytes.write().unwrap() += compound_length;
         }
      } else {
         if compound_length + self.len() > self.configuration.max_storage {
            return StoreResult::StorageFull;
         }
         let mut key_group = KeyGroup::new();
         key_group.push((expiration.clone(), entry.clone()));
         key_groups.insert(key.clone(), key_group);
         *self.size_bytes.write().unwrap() += compound_length;
      }
      StoreResult::Success
   }

   fn compound_length(entry: &StorageEntry) -> usize {
      mem::size_of::<time::Tm>() + match entry {
         &StorageEntry::Value(_) => mem::size_of::<SubotaiHash>(),
         &StorageEntry::Blob(ref blob) => blob.len(),
      }
   }

   //pub fn clear_expired_entries(&self) {
   //   let now = time::now();
   //   let mut extended_entries = self.extended_entries.write().unwrap();
   //   let keys_to_remove: Vec<_>= extended_entries
   //      .iter()
   //      .filter_map(|(key, &ExtendedEntry{ expiration, .. })| if expiration < now { Some(key) } else { None })
   //      .cloned()
   //      .collect();

   //   for key in keys_to_remove {
   //      extended_entries.remove(&key);
   //   }
   //}

   ///// Marks all key-entry pairs as ready for republishing.
   //pub fn mark_all_as_ready(&self) {
   //   for (_, &mut ExtendedEntry {ref mut republish_ready, ..})  in self.extended_entries.write().unwrap().iter_mut() {
   //      *republish_ready = true;
   //   }
   //}

   ///// Marks a particular key-entry pair as not ready for republishing.
   //pub fn mark_as_not_ready(&self, key: &SubotaiHash) {
   //   if let Some( &mut ExtendedEntry {ref mut republish_ready, ..}) = self.extended_entries.write().unwrap().get_mut(key) {
   //      *republish_ready = false;
   //   }
   //}

   ///// Retrieves all key-entry pairs ready for republishing, together with their current expiration date
   //pub fn get_all_ready_entries(&self) -> Vec<(SubotaiHash, StorageEntry, time::Tm)>  {
   //   self.extended_entries.read().unwrap().iter()
   //      .filter(|&(_, &ExtendedEntry{ republish_ready, ..})| republish_ready)
   //      .map(|(key, &ExtendedEntry{ ref entry, ref expiration, ..})| (key.clone(), entry.clone(), expiration.clone()))
   //      .collect()
   //}
}

#[cfg(test)]
mod tests {
   use super::*;
   use {hash, time, node};
}
<|endoftext|>


<commit_before>extern crate curl;

use std;
use std::ascii::AsciiExt;
use std::collections::hash_map::HashMap;
use curl::http;
use curl::http::handle::Method;

pub fn method_from_str(s: &str) -> Method {
    match s.to_ascii_lowercase().as_str() {
        "options" => Method::Options,
        "get" => Method::Get,
        "head" => Method::Head,
        "post" => Method::Post,
        "put" => Method::Put,
        "patch" => Method::Patch,
        "delete" => Method::Delete,
        "trace" => Method::Trace,
        "connect" => Method::Connect,
        _ => { panic!(format!("Invalid method string {}", s)); }
    }
}

pub fn method_to_str(m: Method) -> &'static str {
    match m {
        Method::Options => "Options",
        Method::Get => "Get",
        Method::Head => "Head",
        Method::Post => "Post",
        Method::Put => "Put",
        Method::Patch => "Patch",
        Method::Delete => "Delete",
        Method::Trace => "Trace",
        Method::Connect => "Connect",
    }
}

pub struct SpagRequest {
    pub method: Method,
    pub headers: HashMap<String, String>,
    pub body: String,
    pub endpoint: String,
    pub uri: String,
}

impl SpagRequest {

    pub fn new(method: Method, endpoint: String, uri: String) -> SpagRequest {
        SpagRequest {
            method: method,
            body: String::new(),
            headers: HashMap::new(),
            endpoint: endpoint,
            uri: uri
        }
    }

    pub fn get_method_string(&self) -> &'static str {
        method_to_str(self.method)
    }

    pub fn set_body(&mut self, body: String) {
        self.body = body;
    }

    /// Headers is some iterable of Strings like "Content-type: application/json".
    /// Attempts to split on the ':' and panics if this fails.
    pub fn add_headers<'a, I: Iterator<Item=&'a String>>(&mut self, headers: I) {
        for rawheader in headers {
            // Strip out any spaces and separate the components
            let header = rawheader.replace(" ", "");
            let h: Vec<&str> = header.split(":").collect();

            // If the header didn't have a colon, or had more than 1, it's invalid
            if h.len() != 2 {
                panic!(format!("Invalid header {:?}", header));
            }

            // curl-rust handles content-type in an odd way, so this is necessary
            let lcname = h[0].to_lowercase();
            if lcname == "content-type" {
                self.headers.insert("Content-Type".to_string(), h[1].to_string());
            } else {
                self.headers.insert(h[0].to_string(), h[1].to_string());
            }
        }
    }

    pub fn prepare<'a, 'b>(&'b self, handle: &'a mut http::Handle) -> http::Request<'a, 'b> {
        let uri = self.endpoint.to_string() + &self.uri;
        let headers = self.headers.iter().map(|(a, b)| (a.as_str(), b.as_str()));
        http::Request::new(handle, self.method)
            .uri(uri.to_string())
            .headers(headers)
            .body(&self.body)
    }
}

impl std::fmt::Debug for SpagRequest {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.debug_struct("SpagRequest")
            .field("method", &self.get_method_string())
            .field("headers", &self.headers)
            .field("endpoint", &self.endpoint)
            .field("uri", &self.uri)
            .field("body", &self.body)
            .finish()
    }
}
<commit_msg>Small syntax improvement<commit_after>extern crate curl;

use std;
use std::ascii::AsciiExt;
use std::collections::hash_map::HashMap;
use curl::http;
use curl::http::handle::Method;

pub fn method_from_str(s: &str) -> Method {
    match s.to_ascii_lowercase().as_str() {
        "options" => Method::Options,
        "get" => Method::Get,
        "head" => Method::Head,
        "post" => Method::Post,
        "put" => Method::Put,
        "patch" => Method::Patch,
        "delete" => Method::Delete,
        "trace" => Method::Trace,
        "connect" => Method::Connect,
        _ => { panic!(format!("Invalid method string {}", s)); }
    }
}

pub fn method_to_str(m: Method) -> &'static str {
    match m {
        Method::Options => "Options",
        Method::Get => "Get",
        Method::Head => "Head",
        Method::Post => "Post",
        Method::Put => "Put",
        Method::Patch => "Patch",
        Method::Delete => "Delete",
        Method::Trace => "Trace",
        Method::Connect => "Connect",
    }
}

pub struct SpagRequest {
    pub method: Method,
    pub headers: HashMap<String, String>,
    pub body: String,
    pub endpoint: String,
    pub uri: String,
}

impl SpagRequest {

    pub fn new(method: Method, endpoint: String, uri: String) -> SpagRequest {
        SpagRequest {
            method: method,
            body: String::new(),
            headers: HashMap::new(),
            endpoint: endpoint,
            uri: uri
        }
    }

    pub fn get_method_string(&self) -> &'static str {
        method_to_str(self.method)
    }

    pub fn set_body(&mut self, body: String) {
        self.body = body;
    }

    /// Headers is some iterable of Strings like "Content-type: application/json".
    /// Attempts to split on the ':' and panics if this fails.
    pub fn add_headers<'a, I: Iterator<Item=&'a String>>(&mut self, headers: I) {
        for rawheader in headers {
            // Strip out any spaces and separate the components
            let header = rawheader.replace(" ", "");
            let h: Vec<&str> = header.split(":").collect();

            // If the header didn't have a colon, or had more than 1, it's invalid
            if h.len() != 2 {
                panic!(format!("Invalid header {:?}", header));
            }

            let name =
                if h[0].to_lowercase() == "content-type" {
                    "Content-Type".to_string()
                } else {
                    h[0].to_string()
                };
            let value = h[1].to_string();

            self.headers.insert(name, value);
        }
    }

    pub fn prepare<'a, 'b>(&'b self, handle: &'a mut http::Handle) -> http::Request<'a, 'b> {
        let uri = self.endpoint.to_string() + &self.uri;
        let headers = self.headers.iter().map(|(a, b)| (a.as_str(), b.as_str()));
        http::Request::new(handle, self.method)
            .uri(uri.to_string())
            .headers(headers)
            .body(&self.body)
    }
}

impl std::fmt::Debug for SpagRequest {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.debug_struct("SpagRequest")
            .field("method", &self.get_method_string())
            .field("headers", &self.headers)
            .field("endpoint", &self.endpoint)
            .field("uri", &self.uri)
            .field("body", &self.body)
            .finish()
    }
}
<|endoftext|>


<commit_before>//! Interface for the `signalfd` syscall.
//!
//! # Signal discarding
//! When a signal can't be delivered to a process (or thread), it will become a pending signal.
//! Failure to deliver could happen if the signal is blocked by every thread in the process or if
//! the signal handler is still handling a previous signal.
//!
//! If a signal is sent to a process (or thread) that already has a pending signal of the same
//! type, it will be discarded. This means that if signals of the same type are received faster than
//! they are processed, some of those signals will be dropped. Because of this limitation,
//! `signalfd` in itself cannot be used for reliable communication between processes or threads.
//!
//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending
//! (ie. not consumed from a signalfd) it will be delivered to the signal handler.
//!
//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
//! signal handlers.
use libc;
use crate::unistd;
use crate::{Error, Result};
use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
pub use libc::signalfd_siginfo as siginfo;

use std::os::unix::io::{RawFd, AsRawFd};
use std::mem;

libc_bitflags!{
    pub struct SfdFlags: libc::c_int {
        SFD_NONBLOCK;
        SFD_CLOEXEC;
    }
}

pub const SIGNALFD_NEW: RawFd = -1;
pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<libc::signalfd_siginfo>();

/// Creates a new file descriptor for reading signals.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this function!
///
/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor.
///
/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
/// signalfd (the default handler will be invoked instead).
///
/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
    unsafe {
        Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits()))
    }
}

/// A helper struct for creating, reading and closing a `signalfd` instance.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this struct!
///
/// # Examples
///
/// ```
/// # use nix::sys::signalfd::*;
/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
/// let mut mask = SigSet::empty();
/// mask.add(signal::SIGUSR1);
/// mask.thread_block().unwrap();
///
/// // Signals are queued up on the file descriptor
/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
///
/// match sfd.read_signal() {
///     // we caught a signal
///     Ok(Some(sig)) => (),
///     // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
///     // otherwise the read_signal call blocks)
///     Ok(None) => (),
///     Err(err) => (), // some error happend
/// }
/// ```
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct SignalFd(RawFd);

impl SignalFd {
    pub fn new(mask: &SigSet) -> Result<SignalFd> {
        Self::with_flags(mask, SfdFlags::empty())
    }

    pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
        let fd = signalfd(SIGNALFD_NEW, mask, flags)?;

        Ok(SignalFd(fd))
    }

    pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
        signalfd(self.0, mask, SfdFlags::empty()).map(drop)
    }

    pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
        let mut buffer = mem::MaybeUninit::<[u8; SIGNALFD_SIGINFO_SIZE]>::uninit();

        let res = Errno::result(unsafe {
            libc::read(self.0,
                       buffer.as_mut_ptr() as *mut libc::c_void,
                       SIGNALFD_SIGINFO_SIZE as libc::size_t)
        }).map(|r| r as usize);
        match res {
            Ok(SIGNALFD_SIGINFO_SIZE) => Ok(Some(unsafe { mem::transmute(buffer.assume_init()) })),
            Ok(_) => unreachable!("partial read on signalfd"),
            Err(Errno::EAGAIN) => Ok(None),
            Err(error) => Err(error)
        }
    }
}

impl Drop for SignalFd {
    fn drop(&mut self) {
        let e = unistd::close(self.0);
        if !std::thread::panicking() && e == Err(Error::from(Errno::EBADF)) {
            panic!("Closing an invalid file descriptor!");
        };
    }
}

impl AsRawFd for SignalFd {
    fn as_raw_fd(&self) -> RawFd {
        self.0
    }
}

impl Iterator for SignalFd {
    type Item = siginfo;

    fn next(&mut self) -> Option<Self::Item> {
        match self.read_signal() {
            Ok(Some(sig)) => Some(sig),
            Ok(None) | Err(_) => None,
        }
    }
}

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

    #[test]
    fn create_signalfd() {
        let mask = SigSet::empty();
        let fd = SignalFd::new(&mask);
        assert!(fd.is_ok());
    }

    #[test]
    fn create_signalfd_with_opts() {
        let mask = SigSet::empty();
        let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK);
        assert!(fd.is_ok());
    }

    #[test]
    fn read_empty_signalfd() {
        let mask = SigSet::empty();
        let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();

        let res = fd.read_signal();
        assert!(res.unwrap().is_none());
    }
}
<commit_msg>Don't use mem::transmute in SignalFd<commit_after>//! Interface for the `signalfd` syscall.
//!
//! # Signal discarding
//! When a signal can't be delivered to a process (or thread), it will become a pending signal.
//! Failure to deliver could happen if the signal is blocked by every thread in the process or if
//! the signal handler is still handling a previous signal.
//!
//! If a signal is sent to a process (or thread) that already has a pending signal of the same
//! type, it will be discarded. This means that if signals of the same type are received faster than
//! they are processed, some of those signals will be dropped. Because of this limitation,
//! `signalfd` in itself cannot be used for reliable communication between processes or threads.
//!
//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending
//! (ie. not consumed from a signalfd) it will be delivered to the signal handler.
//!
//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
//! signal handlers.
use libc;
use crate::unistd;
use crate::{Error, Result};
use crate::errno::Errno;
pub use crate::sys::signal::{self, SigSet};
pub use libc::signalfd_siginfo as siginfo;

use std::os::unix::io::{RawFd, AsRawFd};
use std::mem;

libc_bitflags!{
    pub struct SfdFlags: libc::c_int {
        SFD_NONBLOCK;
        SFD_CLOEXEC;
    }
}

pub const SIGNALFD_NEW: RawFd = -1;
#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();

/// Creates a new file descriptor for reading signals.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this function!
///
/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor.
///
/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
/// signalfd (the default handler will be invoked instead).
///
/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
    unsafe {
        Errno::result(libc::signalfd(fd as libc::c_int, mask.as_ref(), flags.bits()))
    }
}

/// A helper struct for creating, reading and closing a `signalfd` instance.
///
/// **Important:** please read the module level documentation about signal discarding before using
/// this struct!
///
/// # Examples
///
/// ```
/// # use nix::sys::signalfd::*;
/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
/// let mut mask = SigSet::empty();
/// mask.add(signal::SIGUSR1);
/// mask.thread_block().unwrap();
///
/// // Signals are queued up on the file descriptor
/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
///
/// match sfd.read_signal() {
///     // we caught a signal
///     Ok(Some(sig)) => (),
///     // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
///     // otherwise the read_signal call blocks)
///     Ok(None) => (),
///     Err(err) => (), // some error happend
/// }
/// ```
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct SignalFd(RawFd);

impl SignalFd {
    pub fn new(mask: &SigSet) -> Result<SignalFd> {
        Self::with_flags(mask, SfdFlags::empty())
    }

    pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
        let fd = signalfd(SIGNALFD_NEW, mask, flags)?;

        Ok(SignalFd(fd))
    }

    pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
        signalfd(self.0, mask, SfdFlags::empty()).map(drop)
    }

    pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
        let mut buffer = mem::MaybeUninit::<siginfo>::uninit();

        let size = mem::size_of_val(&buffer);
        let res = Errno::result(unsafe {
            libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size)
        }).map(|r| r as usize);
        match res {
            Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
            Ok(_) => unreachable!("partial read on signalfd"),
            Err(Errno::EAGAIN) => Ok(None),
            Err(error) => Err(error)
        }
    }
}

impl Drop for SignalFd {
    fn drop(&mut self) {
        let e = unistd::close(self.0);
        if !std::thread::panicking() && e == Err(Error::from(Errno::EBADF)) {
            panic!("Closing an invalid file descriptor!");
        };
    }
}

impl AsRawFd for SignalFd {
    fn as_raw_fd(&self) -> RawFd {
        self.0
    }
}

impl Iterator for SignalFd {
    type Item = siginfo;

    fn next(&mut self) -> Option<Self::Item> {
        match self.read_signal() {
            Ok(Some(sig)) => Some(sig),
            Ok(None) | Err(_) => None,
        }
    }
}

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

    #[test]
    fn create_signalfd() {
        let mask = SigSet::empty();
        let fd = SignalFd::new(&mask);
        assert!(fd.is_ok());
    }

    #[test]
    fn create_signalfd_with_opts() {
        let mask = SigSet::empty();
        let fd = SignalFd::with_flags(&mask, SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK);
        assert!(fd.is_ok());
    }

    #[test]
    fn read_empty_signalfd() {
        let mask = SigSet::empty();
        let mut fd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();

        let res = fd.read_signal();
        assert!(res.unwrap().is_none());
    }
}
<|endoftext|>


<commit_before>use geom::{Cuboid, Rect, Vertex, Vertex2d, Vertex3d};
use geom::tri::{self, Tri};

/// A simple type wrapper around a list of points that describe a polygon.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Polygon<I> {
    /// The iterator yielding all points in the polygon.
    points: I,
}

impl<I> Polygon<I>
where
    I: Iterator,
{
    /// Construct a new polygon from the given list of points describing its vertices.
    pub fn new<P>(points: P) -> Self
    where
        P: IntoIterator<IntoIter = I, Item = I::Item>,
    {
        let points = points.into_iter();
        Polygon { points }
    }

    /// Triangulate the polygon given as a list of `Point`s describing its sides.
    ///
    /// Returns `None` if the polygon's iterator yields less than two points.
    pub fn triangles(self) -> Option<Triangles<I>> {
        triangles(self.points)
    }

    /// Returns `Some` with the touched triangle if the given `Point` is over the polygon described
    /// by the given series of points.
    ///
    /// This uses the `triangles` function internally.
    pub fn contains(self, p: &I::Item) -> Option<Tri<I::Item>>
    where
        I::Item: Vertex2d,
    {
        contains(self.points, p)
    }

    /// The `Rect` that bounds the polygon.
    ///
    /// Returns `None` if the polygon's point iterator is empty.
    pub fn bounding_rect(self) -> Option<Rect<<I::Item as Vertex>::Scalar>>
    where
        I::Item: Vertex2d,
    {
        super::bounding_rect(self.points)
    }

    /// The `Cuboid that bounds the polygon.
    ///
    /// Returns `None` if the polygon's point iterator is empty.
    pub fn bounding_cuboid(self) -> Option<Cuboid<<I::Item as Vertex>::Scalar>>
    where
        I::Item: Vertex3d,
    {
        super::bounding_cuboid(self.points)
    }
}

/// An iterator that triangulates a polygon represented by a sequence of points describing its
/// edges.
#[derive(Clone, Debug)]
pub struct Triangles<I>
where
    I: Iterator,
{
    first: I::Item,
    prev: I::Item,
    points: I,
}

/// Triangulate the polygon given as a list of `Point`s describing its sides.
///
/// Returns `None` if the given iterator yields less than two points.
pub fn triangles<I>(points: I) -> Option<Triangles<I::IntoIter>>
where
    I: IntoIterator,
{
    let mut points = points.into_iter();
    let first = match points.next() {
        Some(p) => p,
        None => return None,
    };
    let prev = match points.next() {
        Some(p) => p,
        None => return None,
    };
    Some(Triangles {
        first: first,
        prev: prev,
        points: points,
    })
}

impl<I> Iterator for Triangles<I>
where
    I: Iterator,
    I::Item: Vertex,
{
    type Item = Tri<I::Item>;
    fn next(&mut self) -> Option<Self::Item> {
        self.points.next().map(|point| {
            let t = Tri([self.first, self.prev, point]);
            self.prev = point;
            t
        })
    }
}

/// Returns `Some` with the touched triangle if the given `Point` is over the polygon described by
/// the given series of points.
///
/// This uses the `triangles` function internally.
pub fn contains<I>(points: I, point: &I::Item) -> Option<Tri<I::Item>>
where
    I: IntoIterator,
    I::Item: Vertex2d,
{
    triangles(points).and_then(|ts| tri::iter_contains(ts, &point))
}
<commit_msg>Make Polygon points field public<commit_after>use geom::{Cuboid, Rect, Vertex, Vertex2d, Vertex3d};
use geom::tri::{self, Tri};

/// A simple type wrapper around a list of points that describe a polygon.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Polygon<I> {
    /// The iterator yielding all points in the polygon.
    pub points: I,
}

impl<I> Polygon<I>
where
    I: Iterator,
{
    /// Construct a new polygon from the given list of points describing its vertices.
    pub fn new<P>(points: P) -> Self
    where
        P: IntoIterator<IntoIter = I, Item = I::Item>,
    {
        let points = points.into_iter();
        Polygon { points }
    }

    /// Triangulate the polygon given as a list of `Point`s describing its sides.
    ///
    /// Returns `None` if the polygon's iterator yields less than two points.
    pub fn triangles(self) -> Option<Triangles<I>> {
        triangles(self.points)
    }

    /// Returns `Some` with the touched triangle if the given `Point` is over the polygon described
    /// by the given series of points.
    ///
    /// This uses the `triangles` function internally.
    pub fn contains(self, p: &I::Item) -> Option<Tri<I::Item>>
    where
        I::Item: Vertex2d,
    {
        contains(self.points, p)
    }

    /// The `Rect` that bounds the polygon.
    ///
    /// Returns `None` if the polygon's point iterator is empty.
    pub fn bounding_rect(self) -> Option<Rect<<I::Item as Vertex>::Scalar>>
    where
        I::Item: Vertex2d,
    {
        super::bounding_rect(self.points)
    }

    /// The `Cuboid that bounds the polygon.
    ///
    /// Returns `None` if the polygon's point iterator is empty.
    pub fn bounding_cuboid(self) -> Option<Cuboid<<I::Item as Vertex>::Scalar>>
    where
        I::Item: Vertex3d,
    {
        super::bounding_cuboid(self.points)
    }
}

/// An iterator that triangulates a polygon represented by a sequence of points describing its
/// edges.
#[derive(Clone, Debug)]
pub struct Triangles<I>
where
    I: Iterator,
{
    first: I::Item,
    prev: I::Item,
    points: I,
}

/// Triangulate the polygon given as a list of `Point`s describing its sides.
///
/// Returns `None` if the given iterator yields less than two points.
pub fn triangles<I>(points: I) -> Option<Triangles<I::IntoIter>>
where
    I: IntoIterator,
{
    let mut points = points.into_iter();
    let first = match points.next() {
        Some(p) => p,
        None => return None,
    };
    let prev = match points.next() {
        Some(p) => p,
        None => return None,
    };
    Some(Triangles {
        first: first,
        prev: prev,
        points: points,
    })
}

impl<I> Iterator for Triangles<I>
where
    I: Iterator,
    I::Item: Vertex,
{
    type Item = Tri<I::Item>;
    fn next(&mut self) -> Option<Self::Item> {
        self.points.next().map(|point| {
            let t = Tri([self.first, self.prev, point]);
            self.prev = point;
            t
        })
    }
}

/// Returns `Some` with the touched triangle if the given `Point` is over the polygon described by
/// the given series of points.
///
/// This uses the `triangles` function internally.
pub fn contains<I>(points: I, point: &I::Item) -> Option<Tri<I::Item>>
where
    I: IntoIterator,
    I::Item: Vertex2d,
{
    triangles(points).and_then(|ts| tri::iter_contains(ts, &point))
}
<|endoftext|>


<commit_before>#![cfg(feature = "use_std")]

use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;

/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    GroupingMap { iter }
}

/// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
/// It groups elements by their key and at the same time fold each group
/// using some aggregating operation.
///
/// No method on this struct performs temporary allocations.
pub struct GroupingMap<I> {
    iter: I,
}

impl<I, K, V> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    /// This is the generic way to perform any operations on a `GroupingMap`.
    /// It's suggested to use this method only to implement custom operations
    /// when the already provided ones are not enough.
    ///
    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in an `HashMap`.
    ///
    /// The `operation` function is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group if there is currently one;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being aggregated;
    /// If `operation` returns `Some(element)` then the accumulator is updated with `element`,
    /// otherwise the previous accumulation is discarded.
    ///
    /// Return a `HashMap` associating the key of each group with the result of aggregation of the group elements.
    /// If there's no result then there won't be an entry associated to that key.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10];
    /// let lookup = data.into_iter()
    ///     .map(|n| (n % 4, n))
    ///     .into_grouping_map()
    ///     .aggregate(|acc, _, val| {
    ///         if val == 0 || val == 10 {
    ///             None
    ///         } else {
    ///             Some(acc.unwrap_or(0) + val)
    ///         }
    ///     });
    ///
    /// assert_eq!(lookup[&0], 4);        // 0 resets the accumulator so only 4 is summed
    /// assert_eq!(lookup[&1], 14);       // 5 + 9
    /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward
    /// assert_eq!(lookup[&3], 7);        // 7
    /// assert_eq!(lookup.len(), 3);      // The final keys are only 0, 1 and 2
    /// ```
    pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
    where
        FO: FnMut(Option<R>, &K, V) -> Option<R>,
    {
        let mut destination_map = HashMap::new();

        for (key, val) in self.iter {
            let acc = destination_map.remove(&key);
            if let Some(op_res) = operation(acc, &key, val) {
                destination_map.insert(key, op_res);
            }
        }

        destination_map
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// `init` is the value from which will be cloned the initial value of each accumulator.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = (1..=7)
    ///     .map(|n| (n % 3, n))
    ///     .into_grouping_map()
    ///     .fold(0, |acc, _, val| acc + val);
    ///
    /// assert_eq!(lookup[&0], 9);   // 3 + 6
    /// assert_eq!(lookup[&1], 12);  // 1 + 4 + 7
    /// assert_eq!(lookup[&2], 7);   // 2 + 5
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
    where
        R: Clone,
        FO: FnMut(R, &K, V) -> R,
    {
        self.aggregate(|acc, key, val| {
            let acc = acc.unwrap_or_else(|| init.clone());
            Some(operation(acc, key, val))
        })
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// [`fold`]: #tymethod.fold
    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
    where
        FO: FnMut(V, &K, V) -> V,
    {
        self.aggregate(|acc, key, val| {
            Some(match acc {
                Some(acc) => operation(acc, key, val),
                None => val,
            })
        })
    }

    /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
    /// an instance of `C`. The iteration order is preserved when inserting elements.
    ///
    /// Return a `HashMap` associating the key of each group with the collection containing the elements of that group.
    pub fn collect<C>(self) -> HashMap<K, C>
    where
        C: Default + Extend<V>,
    {
        self.aggregate(|acc, _, v| {
            let mut acc = acc.unwrap_or_else(C::default);
            acc.extend(Some(v));
            Some(acc)
        })
    }

    /// Groups elements from the `GroupingMap` source by key and counts them.
    ///
    /// Return a `HashMap` associating the key of each group with the number of elements in that group.
    pub fn count(self) -> HashMap<K, usize> {
        self.fold(0, |acc, _, _| acc + 1)
    }
}
<commit_msg>Added doctest for GroupingMap::fold_first<commit_after>#![cfg(feature = "use_std")]

use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;

/// Creates a new `GroupingMap` from `iter`
pub fn new<I, K, V>(iter: I) -> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    GroupingMap { iter }
}

/// `GroupingMap` is an intermediate struct for efficient group-and-fold operations.
/// It groups elements by their key and at the same time fold each group
/// using some aggregating operation.
///
/// No method on this struct performs temporary allocations.
pub struct GroupingMap<I> {
    iter: I,
}

impl<I, K, V> GroupingMap<I>
where
    I: Iterator<Item = (K, V)>,
    K: Hash + Eq,
{
    /// This is the generic way to perform any operations on a `GroupingMap`.
    /// It's suggested to use this method only to implement custom operations
    /// when the already provided ones are not enough.
    ///
    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in an `HashMap`.
    ///
    /// The `operation` function is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group if there is currently one;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being aggregated;
    /// If `operation` returns `Some(element)` then the accumulator is updated with `element`,
    /// otherwise the previous accumulation is discarded.
    ///
    /// Return a `HashMap` associating the key of each group with the result of aggregation of the group elements.
    /// If there's no result then there won't be an entry associated to that key.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let data = vec![2, 8, 5, 7, 9, 0, 4, 10];
    /// let lookup = data.into_iter()
    ///     .map(|n| (n % 4, n))
    ///     .into_grouping_map()
    ///     .aggregate(|acc, _, val| {
    ///         if val == 0 || val == 10 {
    ///             None
    ///         } else {
    ///             Some(acc.unwrap_or(0) + val)
    ///         }
    ///     });
    ///
    /// assert_eq!(lookup[&0], 4);        // 0 resets the accumulator so only 4 is summed
    /// assert_eq!(lookup[&1], 14);       // 5 + 9
    /// assert_eq!(lookup.get(&2), None); // 10 resets the accumulator and nothing is summed afterward
    /// assert_eq!(lookup[&3], 7);        // 7
    /// assert_eq!(lookup.len(), 3);      // The final keys are only 0, 1 and 2
    /// ```
    pub fn aggregate<FO, R>(self, mut operation: FO) -> HashMap<K, R>
    where
        FO: FnMut(Option<R>, &K, V) -> Option<R>,
    {
        let mut destination_map = HashMap::new();

        for (key, val) in self.iter {
            let acc = destination_map.remove(&key);
            if let Some(op_res) = operation(acc, &key, val) {
                destination_map.insert(key, op_res);
            }
        }

        destination_map
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// `init` is the value from which will be cloned the initial value of each accumulator.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = (1..=7)
    ///     .map(|n| (n % 3, n))
    ///     .into_grouping_map()
    ///     .fold(0, |acc, _, val| acc + val);
    ///
    /// assert_eq!(lookup[&0], 9);   // 3 + 6
    /// assert_eq!(lookup[&1], 12);  // 1 + 4 + 7
    /// assert_eq!(lookup[&2], 7);   // 2 + 5
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn fold<FO, R>(self, init: R, mut operation: FO) -> HashMap<K, R>
    where
        R: Clone,
        FO: FnMut(R, &K, V) -> R,
    {
        self.aggregate(|acc, key, val| {
            let acc = acc.unwrap_or_else(|| init.clone());
            Some(operation(acc, key, val))
        })
    }

    /// Groups elements from the `GroupingMap` source by key and applies `operation` to the elements
    /// of each group sequentially, passing the previously accumulated value, a reference to the key
    /// and the current element as arguments, and stores the results in a new map.
    ///
    /// This is similar to [`fold`] but the initial value of the accumulator is the first element of the group.
    ///
    /// `operation` is a function that is invoked on each element with the following parameters:
    ///  - the current value of the accumulator of the group;
    ///  - a reference to the key of the group this element belongs to;
    ///  - the element from the source being accumulated.
    ///
    /// Return a `HashMap` associating the key of each group with the result of folding the group elements.
    ///
    /// [`fold`]: #tymethod.fold
    ///
    /// ```
    /// use itertools::Itertools;
    ///
    /// let lookup = (1..=7)
    ///     .map(|n| (n % 3, n))
    ///     .into_grouping_map()
    ///     .fold_first(|acc, _, val| acc + val);
    ///
    /// assert_eq!(lookup[&0], 9);   // 3 + 6
    /// assert_eq!(lookup[&1], 12);  // 1 + 4 + 7
    /// assert_eq!(lookup[&2], 7);   // 2 + 5
    /// assert_eq!(lookup.len(), 3);
    /// ```
    pub fn fold_first<FO>(self, mut operation: FO) -> HashMap<K, V>
    where
        FO: FnMut(V, &K, V) -> V,
    {
        self.aggregate(|acc, key, val| {
            Some(match acc {
                Some(acc) => operation(acc, key, val),
                None => val,
            })
        })
    }

    /// Groups elements from the `GroupingMap` source by key and collects the elements of each group in
    /// an instance of `C`. The iteration order is preserved when inserting elements.
    ///
    /// Return a `HashMap` associating the key of each group with the collection containing the elements of that group.
    pub fn collect<C>(self) -> HashMap<K, C>
    where
        C: Default + Extend<V>,
    {
        self.aggregate(|acc, _, v| {
            let mut acc = acc.unwrap_or_else(C::default);
            acc.extend(Some(v));
            Some(acc)
        })
    }

    /// Groups elements from the `GroupingMap` source by key and counts them.
    ///
    /// Return a `HashMap` associating the key of each group with the number of elements in that group.
    pub fn count(self) -> HashMap<K, usize> {
        self.fold(0, |acc, _, _| acc + 1)
    }
}
<|endoftext|>


<commit_before>use chan;
use chan::Sender;
use rustc_serialize::json;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use ws;
use ws::{CloseCode, Handler, Handshake, Message, Sender as WsSender};
use ws::util::Token;

use datatype::{Command, Error, Event};
use super::gateway::{Gateway, Interpret};

/// The `Websocket` gateway allows connected clients to listen to `Event`s that
/// happen in the SOTA client.
pub struct Websocket {
    pub server:  String,
    pub clients: Arc<Mutex<HashMap<Token, WsSender>>>
}

impl Gateway for Websocket {
    fn initialize(&mut self, itx: Sender<Interpret>) -> Result<(), String> {
        ws::listen(&self.server.clone() as &str, |out| {
            WebsocketHandler {
                out:     out,
                itx:     itx.clone(),
                clients: self.clients.clone()
            }
        }).expect("couldn't start websocket listener");

        Ok(info!("Websocket gateway started at {}.", self.server))
    }

    fn pulse(&self, event: Event) {
        let json = encode(event);
        for (_, out) in self.clients.lock().unwrap().iter() {
            let _ = out.send(Message::Text(json.clone()));
        }
    }
}

pub struct WebsocketHandler {
    out:     WsSender,
    itx:     Sender<Interpret>,
    clients: Arc<Mutex<HashMap<Token, WsSender>>>
}

impl Handler for WebsocketHandler {
    fn on_message(&mut self, msg: Message) -> ws::Result<()> {
        debug!("received websocket message: {:?}", msg);
        msg.as_text().or_else(|err| {
            error!("websocket on_message text error: {}", err);
            Err(err)
        }).and_then(|msg| match decode(msg) {
            Ok(cmd) => Ok(self.forward_command(cmd)),

            Err(Error::Websocket(err)) => {
                error!("websocket on_message error: {}", err);
                Err(err)
            }

            Err(err) => panic!("unexpected websocket on_message error: {}", err)
        })
    }

    fn on_open(&mut self, _: Handshake) -> ws::Result<()> {
        let _ = self.clients.lock().unwrap().insert(self.out.token(), self.out.clone());
        Ok(debug!("new websocket client: {:?}", self.out.token()))
    }

    fn on_close(&mut self, code: CloseCode, _: &str) {
        let _ = self.clients.lock().unwrap().remove(&self.out.token());
        debug!("closing websocket client {:?}: {:?}", self.out.token(), code);
    }

    fn on_error(&mut self, err: ws::Error) {
        error!("websocket error: {:?}", err);
    }
}

impl WebsocketHandler {
    fn forward_command(&self, cmd: Command) {
        let (etx, erx) = chan::sync::<Event>(0);
        let etx        = Arc::new(Mutex::new(etx.clone()));
        self.itx.send(Interpret { command: cmd, response_tx: Some(etx) });

        let e = erx.recv().expect("websocket response_tx is closed");
        let _ = self.out.send(Message::Text(encode(e)));
    }
}

fn encode(event: Event) -> String {
    json::encode(&event).expect("Error encoding event into JSON")
}

fn decode(s: &str) -> Result<Command, Error> {
    Ok(try!(json::decode::<Command>(s)))
}

#[cfg(test)]
mod tests {
    use chan;
    use crossbeam;
    use rustc_serialize::json;
    use std::thread;
    use std::collections::HashMap;
    use std::sync::{Arc, Mutex};
    use ws;
    use ws::CloseCode;

    use datatype::{Command, Event};
    use gateway::{Gateway, Interpret};
    use super::*;

    #[test]
    #[ignore] // FIXME: wait for https://github.com/housleyjk/ws-rs/issues/64
    fn websocket_connections() {
        let (etx, erx) = chan::sync::<Event>(0);
        let (itx, irx) = chan::sync::<Interpret>(0);

        thread::spawn(move || {
            Websocket {
                server:  "localhost:3012".to_string(),
                clients: Arc::new(Mutex::new(HashMap::new()))
            }.start(itx, erx);
        });
        thread::spawn(move || {
            let _ = etx; // move into this scope
            loop {
                let interpret = irx.recv().expect("gtx is closed");
                match interpret.command {
                    Command::StartDownload(id) => {
                        let tx = interpret.response_tx.unwrap();
                        tx.lock().unwrap().send(Event::FoundSystemInfo(id));
                    }
                    _ => panic!("expected AcceptUpdates"),
                }
            }
        });

        crossbeam::scope(|scope| {
            for id in 0..10 {
                scope.spawn(move || {
                    ws::connect("ws://localhost:3012", |out| {
                        let msg = format!(r#"{{ "variant": "StartDownload", "fields": [["{}"]] }}"#, id);
                        out.send(msg).expect("couldn't write to websocket");

                        move |msg: ws::Message| {
                            let ev: Event = json::decode(&format!("{}", msg)).unwrap();
                            assert_eq!(ev, Event::FoundSystemInfo(format!("{}", id)));
                            out.close(CloseCode::Normal)
                        }
                    }).expect("couldn't connect to websocket");
                });
            }
        });
    }
}
<commit_msg>Fix blocking Websocket gateway listen call<commit_after>use chan;
use chan::Sender;
use rustc_serialize::json;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
use ws;
use ws::{CloseCode, Handler, Handshake, Message, Sender as WsSender};
use ws::util::Token;

use datatype::{Command, Error, Event};
use super::gateway::{Gateway, Interpret};

/// The `Websocket` gateway allows connected clients to listen to `Event`s that
/// happen in the SOTA client.
pub struct Websocket {
    pub server:  String,
    pub clients: Arc<Mutex<HashMap<Token, WsSender>>>
}

impl Gateway for Websocket {
    fn initialize(&mut self, itx: Sender<Interpret>) -> Result<(), String> {
        let clients = self.clients.clone();
        let addr    = self.server.clone();

        thread::spawn(move || {
            ws::listen(&addr as &str, |out| {
                WebsocketHandler {
                    out:     out,
                    itx:     itx.clone(),
                    clients: clients.clone()
                }
            }).expect("couldn't start websocket listener");
        });

        Ok(info!("Websocket gateway started at {}.", self.server))
    }

    fn pulse(&self, event: Event) {
        let json = encode(event);
        for (_, out) in self.clients.lock().unwrap().iter() {
            let _ = out.send(Message::Text(json.clone()));
        }
    }
}

pub struct WebsocketHandler {
    out:     WsSender,
    itx:     Sender<Interpret>,
    clients: Arc<Mutex<HashMap<Token, WsSender>>>
}

impl Handler for WebsocketHandler {
    fn on_message(&mut self, msg: Message) -> ws::Result<()> {
        debug!("received websocket message: {:?}", msg);
        msg.as_text().or_else(|err| {
            error!("websocket on_message text error: {}", err);
            Err(err)
        }).and_then(|msg| match decode(msg) {
            Ok(cmd) => Ok(self.forward_command(cmd)),

            Err(Error::Websocket(err)) => {
                error!("websocket on_message error: {}", err);
                Err(err)
            }

            Err(err) => panic!("unexpected websocket on_message error: {}", err)
        })
    }

    fn on_open(&mut self, _: Handshake) -> ws::Result<()> {
        let _ = self.clients.lock().unwrap().insert(self.out.token(), self.out.clone());
        Ok(debug!("new websocket client: {:?}", self.out.token()))
    }

    fn on_close(&mut self, code: CloseCode, _: &str) {
        let _ = self.clients.lock().unwrap().remove(&self.out.token());
        debug!("closing websocket client {:?}: {:?}", self.out.token(), code);
    }

    fn on_error(&mut self, err: ws::Error) {
        error!("websocket error: {:?}", err);
    }
}

impl WebsocketHandler {
    fn forward_command(&self, cmd: Command) {
        let (etx, erx) = chan::sync::<Event>(0);
        let etx        = Arc::new(Mutex::new(etx.clone()));
        self.itx.send(Interpret { command: cmd, response_tx: Some(etx) });

        let e = erx.recv().expect("websocket response_tx is closed");
        let _ = self.out.send(Message::Text(encode(e)));
    }
}

fn encode(event: Event) -> String {
    json::encode(&event).expect("Error encoding event into JSON")
}

fn decode(s: &str) -> Result<Command, Error> {
    Ok(try!(json::decode::<Command>(s)))
}

#[cfg(test)]
mod tests {
    use chan;
    use crossbeam;
    use rustc_serialize::json;
    use std::thread;
    use std::collections::HashMap;
    use std::sync::{Arc, Mutex};
    use ws;
    use ws::CloseCode;

    use datatype::{Command, Event};
    use gateway::{Gateway, Interpret};
    use super::*;

    #[test]
    #[ignore] // FIXME: wait for https://github.com/housleyjk/ws-rs/issues/64
    fn websocket_connections() {
        let (etx, erx) = chan::sync::<Event>(0);
        let (itx, irx) = chan::sync::<Interpret>(0);

        thread::spawn(move || {
            Websocket {
                server:  "localhost:3012".to_string(),
                clients: Arc::new(Mutex::new(HashMap::new()))
            }.start(itx, erx);
        });
        thread::spawn(move || {
            let _ = etx; // move into this scope
            loop {
                let interpret = irx.recv().expect("gtx is closed");
                match interpret.command {
                    Command::StartDownload(id) => {
                        let tx = interpret.response_tx.unwrap();
                        tx.lock().unwrap().send(Event::FoundSystemInfo(id));
                    }
                    _ => panic!("expected AcceptUpdates"),
                }
            }
        });

        crossbeam::scope(|scope| {
            for id in 0..10 {
                scope.spawn(move || {
                    ws::connect("ws://localhost:3012", |out| {
                        let msg = format!(r#"{{ "variant": "StartDownload", "fields": [["{}"]] }}"#, id);
                        out.send(msg).expect("couldn't write to websocket");

                        move |msg: ws::Message| {
                            let ev: Event = json::decode(&format!("{}", msg)).unwrap();
                            assert_eq!(ev, Event::FoundSystemInfo(format!("{}", id)));
                            out.close(CloseCode::Normal)
                        }
                    }).expect("couldn't connect to websocket");
                });
            }
        });
    }
}
<|endoftext|>


<commit_before>//! Generate test cases from the JSON Schema Test Suite.

use inflector::Inflector;
use serde::{Deserialize, Serialize};
use std::{error::Error, ffi::OsStr, fs, path::PathBuf};

// Each test has a description, schema, and a list of tests. Each of
// those tests has a description, some data, and a `valid` field which
// indicates whether that data should validate against the schema.
schemafy::schemafy!(
    root: TestSchema
    "tests/JSON-Schema-Test-Suite/test-schema.json"
);

fn main() -> Result<(), Box<dyn Error>> {
    let test_suite_dir = PathBuf::from("tests/test_suite");
    let schemas_dir = test_suite_dir.join("schemas");
    if test_suite_dir.exists() {
        fs::remove_dir_all("tests/test_suite")?;
    }
    fs::create_dir(&test_suite_dir)?;
    fs::create_dir(&schemas_dir)?;

    let mut test_modules = vec![];
    let mut blacklist_count = 0;

    for path in fs::read_dir("tests/JSON-Schema-Test-Suite/tests/draft4")?
        .map(|entry| entry.unwrap().path())
        .filter(|path| path.extension() == Some(OsStr::new("json")))
    {
        let buffer = fs::read_to_string(&path)?;
        let test_schema: TestSchema = serde_json::from_str(&buffer)?;
        println!("{} ==> {} tests", path.display(), test_schema.len());

        let module_name = path.file_stem().unwrap().to_str().unwrap().to_snake_case();

        let mut test_file: String = format!(
            r#"//! Automatically generated from {}
"#,
            path.display()
        );

        for (i, test_group) in test_schema.iter().enumerate() {
            if is_blacklisted(&module_name, i) {
                blacklist_count += 1;
                println!(" !! skipping test group: {}", test_group.description);
                continue;
            }

            let schema_name = format!("{}_{}.json", module_name, i);
            let schema = serde_json::to_string(&test_group.schema)?;
            fs::write(schemas_dir.join(&schema_name), schema)?;

            test_file.push_str(&format!(
                r#"
mod {} {{
    #[allow(unused_imports)]
    use serde::{{Deserialize, Serialize}};

    schemafy::schemafy!(root: Schema "tests/test_suite/schemas/{}");
"#,
                test_group.description.to_snake_case(),
                schema_name
            ));
            for test in &test_group.tests {
                let test_name = {
                    // Prefix the name with an underscore if it starts
                    // with a number.
                    let root = test.description.to_snake_case();
                    let prefix = match root.chars().next().unwrap().is_numeric() {
                        true => "_",
                        false => "",
                    };
                    format!("{}{}", prefix, root)
                };

                // For the positive test cases, unwrapping the result
                // gives better error messages than simply asserting
                // on .is_ok(). For the negative test cases, a simple
                // assert is the best we can do.
                let assertion = match test.valid {
                    true => "let _: Schema = serde_json::from_str(&data).unwrap();",
                    false => "assert!(serde_json::from_str::<Schema>(&data).is_err());",
                };

                test_file.push_str(&format!(
                    r##"
    #[test]
    fn r#{}() {{
        let data = r#"{}"#;
        {}
    }}
"##,
                    test_name, test.data, assertion
                ));
            }
            test_file.push_str("}\n");
        }

        fs::write(
            test_suite_dir.join(format!("{}.rs", module_name)),
            test_file,
        )?;
        test_modules.push(module_name);
    }

    // Generate a root module that declares all the above files.
    let mut tests: String = r#"//! Automatically generated
"#
    .into();
    for module in &test_modules {
        tests.push_str(&format!("mod r#{};\n", module));
    }
    fs::write(test_suite_dir.join("mod.rs"), tests)?;

    if blacklist_count > 0 {
        println!("\nSkipped {} test schemas\n", blacklist_count);
    }

    Ok(())
}

/// To allow for gradual progress, this function determines whether a
/// test should be skipped.
fn is_blacklisted(test_group: &str, index: usize) -> bool {
    match test_group {
        "additional_items" if index == 0 || index == 2 => true,

        "additional_properties"
        | "all_of"
        | "any_of"
        | "definitions"
        | "dependencies"
        | "enum"
        | "items"
        | "max_items"
        | "max_length"
        | "max_properties"
        | "maximum"
        | "min_items"
        | "min_length"
        | "min_properties"
        | "minimum"
        | "multiple_of"
        | "not"
        | "one_of"
        | "pattern"
        | "pattern_properties"
        | "properties"
        | "ref"
        | "ref_remote"
        | "required"
        | "type"
        | "unique_items" => true,

        _ => false,
    }
}
<commit_msg>Enable more tests from the JSON schema test suite<commit_after>//! Generate test cases from the JSON Schema Test Suite.

use inflector::Inflector;
use serde::{Deserialize, Serialize};
use std::{error::Error, ffi::OsStr, fs, path::PathBuf};

// Each test has a description, schema, and a list of tests. Each of
// those tests has a description, some data, and a `valid` field which
// indicates whether that data should validate against the schema.
schemafy::schemafy!(
    root: TestSchema
    "tests/JSON-Schema-Test-Suite/test-schema.json"
);

fn main() -> Result<(), Box<dyn Error>> {
    let test_suite_dir = PathBuf::from("tests/test_suite");
    let schemas_dir = test_suite_dir.join("schemas");
    if test_suite_dir.exists() {
        fs::remove_dir_all("tests/test_suite")?;
    }
    fs::create_dir(&test_suite_dir)?;
    fs::create_dir(&schemas_dir)?;

    let mut test_modules = vec![];
    let mut blacklist_count = 0;

    for path in fs::read_dir("tests/JSON-Schema-Test-Suite/tests/draft4")?
        .map(|entry| entry.unwrap().path())
        .filter(|path| path.extension() == Some(OsStr::new("json")))
    {
        let buffer = fs::read_to_string(&path)?;
        let test_schema: TestSchema = serde_json::from_str(&buffer)?;
        println!("{} ==> {} tests", path.display(), test_schema.len());

        let module_name = path.file_stem().unwrap().to_str().unwrap().to_snake_case();

        let mut test_file: String = format!(
            r#"//! Automatically generated from {}
"#,
            path.display()
        );

        for (i, test_group) in test_schema.iter().enumerate() {
            if is_blacklisted(&module_name, i) {
                blacklist_count += 1;
                println!(" !! skipping test group: {}", test_group.description);
                continue;
            }

            let schema_name = format!("{}_{}.json", module_name, i);
            let schema = serde_json::to_string(&test_group.schema)?;
            fs::write(schemas_dir.join(&schema_name), schema)?;

            test_file.push_str(&format!(
                r#"
mod _{}_{} {{
    #[allow(unused_imports)]
    use serde::{{Deserialize, Serialize}};

    schemafy::schemafy!(root: Schema "tests/test_suite/schemas/{}");
"#,
                i,
                test_group.description.to_snake_case(),
                schema_name
            ));
            for test in &test_group.tests {
                let test_name = {
                    // Prefix the name with an underscore if it starts
                    // with a number.
                    let root = test.description.to_snake_case();
                    let prefix = match root.chars().next().unwrap().is_numeric() {
                        true => "_",
                        false => "",
                    };
                    format!("{}{}", prefix, root)
                };

                // For the positive test cases, unwrapping the result
                // gives better error messages than simply asserting
                // on .is_ok(). For the negative test cases, a simple
                // assert is the best we can do.
                let assertion = match test.valid {
                    true => "let _: Schema = serde_json::from_str(&data).unwrap();",
                    false => "assert!(serde_json::from_str::<Schema>(&data).is_err());",
                };

                test_file.push_str(&format!(
                    r##"
    #[test]
    fn r#{}() {{
        let data = r#"{}"#;
        {}
    }}
"##,
                    test_name, test.data, assertion
                ));
            }
            test_file.push_str("}\n");
        }

        fs::write(
            test_suite_dir.join(format!("{}.rs", module_name)),
            test_file,
        )?;
        test_modules.push(module_name);
    }

    // Generate a root module that declares all the above files.
    let mut tests: String = r#"//! Automatically generated
"#
    .into();
    for module in &test_modules {
        tests.push_str(&format!("mod r#{};\n", module));
    }
    fs::write(test_suite_dir.join("mod.rs"), tests)?;

    if blacklist_count > 0 {
        println!("\nSkipped {} test schemas\n", blacklist_count);
    }

    Ok(())
}

/// To allow for gradual progress, this function determines whether a
/// test should be skipped.
fn is_blacklisted(test_group: &str, index: usize) -> bool {
    let blacklisted_indices: &[usize] = match test_group {
        "additional_items" => &[0, 2],
        "additional_properties" => &[0, 1, 2, 3, 5],
        "all_of" => &[1, 2, 5, 6],
        "any_of" => &[0, 1, 2, 4],
        "definitions" => &[0, 1],
        "dependencies" => &[0, 1, 2, 3],
        "enum" => &[0, 1, 3, 4, 5, 6, 7],
        "items" => &[0, 1, 2],
        "max_items" => &[0],
        "max_length" => &[0],
        "max_properties" => &[0],
        "maximum" => &[0, 1, 2],
        "min_items" => &[0],
        "min_length" => &[0],
        "min_properties" => &[0],
        "minimum" => &[0, 1, 2, 3],
        "multiple_of" => &[0, 1, 2],
        "not" => &[0, 1, 2, 3],
        "one_of" => &[0, 1, 2, 3, 4],
        "pattern" => &[0],
        "pattern_properties" => &[0, 1, 2],
        "properties" => &[0, 1, 2],
        "ref" => &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
        "ref_remote" => &[0, 1, 2, 3, 4, 5, 6],
        "required" => &[0, 2],
        "type" => &[6, 7, 9, 10],
        "unique_items" => &[0, 1, 2],

        _ => &[],
    };

    blacklisted_indices.contains(&index)
}
<|endoftext|>