<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::ValidationMessage;
use crate::match_::SplitOperationMetadata;
use crate::util::{get_fragment_filename, get_normalization_operation_name};
use common::{Diagnostic, DiagnosticsResult, NamedItem, WithLocation};
use fnv::{FnvHashMap, FnvHashSet};
use graphql_ir::{
    Argument, ConstantValue, Directive, FragmentDefinition, FragmentSpread, OperationDefinition,
    Program, Selection, Transformed, Transformer, Value,
};
use graphql_syntax::OperationKind;
use indexmap::IndexSet;
use interner::{Intern, StringKey};
use lazy_static::lazy_static;
use schema::{InterfaceID, Schema, Type};
use std::sync::Arc;

lazy_static! {
    pub static ref RELAY_CLIENT_COMPONENT_SERVER_DIRECTIVE_NAME: StringKey =
        "relay_client_component_server".intern();
    pub static ref RELAY_CLIENT_COMPONENT_MODULE_ID_ARGUMENT_NAME: StringKey = "module_id".intern();
    pub static ref RELAY_CLIENT_COMPONENT_METADATA_KEY: StringKey =
        "__RelayClientComponentMetadata".intern();
    pub static ref RELAY_CLIENT_COMPONENT_METADATA_SPLIT_OPERATION_ARG_KEY: StringKey =
        "__RelayClientComponentMetadataSplitOperation".intern();
    static ref RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME: StringKey = "relay_client_component".intern();
    static ref STRING_TYPE: StringKey = "String".intern();
    static ref ID_FIELD_NAME: StringKey = "id".intern();
    static ref NODE_TYPE_NAME: StringKey = "Node".intern();
    static ref VIEWER_TYPE_NAME: StringKey = "Viewer".intern();
    static ref DIRECTIVE_COMPATIBILITY_ALLOWLIST: IndexSet<StringKey> = IndexSet::new();
}

pub fn relay_client_component(program: &Program) -> DiagnosticsResult<Program> {
    // Noop, the @relay_client_component_server directive is not defined in the schema
    if program
        .schema
        .get_directive(*RELAY_CLIENT_COMPONENT_SERVER_DIRECTIVE_NAME)
        .is_none()
    {
        return Ok(program.clone());
    }
    let node_interface_id = program
        .schema
        .get_type(*NODE_TYPE_NAME)
        .and_then(|type_| {
            if let Type::Interface(id) = type_ {
                Some(id)
            } else {
                None
            }
        })
        .expect("@relay_client_component requires your schema to define the Node interface.");

    let mut transform = RelayClientComponentTransform::new(program, node_interface_id);
    let mut next_program = transform
        .transform_program(program)
        .replace_or_else(|| program.clone());

    if !transform.split_operations.is_empty() {
        for (_, (metadata, mut operation)) in transform.split_operations.drain() {
            operation.directives.push(metadata.to_directive());
            if let Some(prev_operation) = next_program.operation(operation.name.item) {
                transform.errors.push(Diagnostic::error(
                    ValidationMessage::DuplicateRelayClientComponentSplitOperation,
                    prev_operation.name.location,
                ));
            } else {
                next_program.insert_operation(Arc::new(operation))
            }
        }
    }

    if transform.errors.is_empty() {
        Ok(next_program)
    } else {
        Err(transform.errors)
    }
}

struct RelayClientComponentTransform<'program> {
    program: &'program Program,
    errors: Vec<Diagnostic>,
    split_operations: FnvHashMap<StringKey, (SplitOperationMetadata, OperationDefinition)>,
    node_interface_id: InterfaceID,
    split_operation_filenames: FnvHashSet<StringKey>,
}

impl<'program> RelayClientComponentTransform<'program> {
    fn new(program: &'program Program, node_interface_id: InterfaceID) -> Self {
        Self {
            program,
            errors: Default::default(),
            split_operations: Default::default(),
            node_interface_id,
            split_operation_filenames: Default::default(),
        }
    }

    fn transform_relay_client_component(
        &mut self,
        spread: &FragmentSpread,
    ) -> Result<Transformed<Selection>, Diagnostic> {
        if let Some(incompatible_directives_diagnostic) =
            self.get_incompatible_directives_diagnostic(spread)
        {
            return Err(incompatible_directives_diagnostic);
        }
        // @relay_client_component does not take arguments (yet)
        if let Some(argument) = spread.arguments.first() {
            return Err(Diagnostic::error(
                ValidationMessage::InvalidRelayClientComponentWithArguments,
                argument.name.location,
            ));
        }

        let fragment = self
            .program
            .fragment(spread.fragment.item)
            .unwrap_or_else(|| panic!("Expected to find fragment `{}`", spread.fragment.item));
        // Validate that the fragment's type condition MUST implement `Node`.
        let node_interface_id = self.node_interface_id;
        let implements_node = match fragment.type_condition {
            // Fragments can be specified on object types, interfaces, and unions.
            // https://spec.graphql.org/June2018/#sec-Type-Conditions
            Type::Interface(id) => {
                id == node_interface_id
                    || self
                        .program
                        .schema
                        .interface(id)
                        .implementing_objects
                        .iter()
                        .all(|&object_id| {
                            self.program
                                .schema
                                .object(object_id)
                                .interfaces
                                .iter()
                                .any(|interface_id| *interface_id == node_interface_id)
                        })
            }
            Type::Object(id) => self
                .program
                .schema
                .object(id)
                .interfaces
                .iter()
                .any(|interface_id| *interface_id == node_interface_id),
            Type::Union(id) => self
                .program
                .schema
                .union(id)
                .members
                .iter()
                .all(|&object_id| {
                    self.program
                        .schema
                        .object(object_id)
                        .interfaces
                        .iter()
                        .any(|interface_id| *interface_id == node_interface_id)
                }),
            _ => false,
        };
        let is_fragment_on_query =
            fragment.type_condition == self.program.schema.query_type().unwrap();
        let is_fragment_on_viewer =
            self.program.schema.get_type_name(fragment.type_condition) == *VIEWER_TYPE_NAME;
        if !implements_node && !is_fragment_on_query && !is_fragment_on_viewer {
            return Err(Diagnostic::error(
                ValidationMessage::InvalidRelayClientComponentNonNodeFragment,
                fragment.name.location,
            ));
        }

        // Generate a SplitOperation AST
        let normalization_name = get_normalization_operation_name(spread.fragment.item).intern();
        let created_split_operation = self
            .split_operations
            .entry(spread.fragment.item)
            .or_insert_with(|| {
                (
                    SplitOperationMetadata {
                        derived_from: spread.fragment.item,
                        parent_sources: Default::default(),
                        raw_response_type: false,
                    },
                    OperationDefinition {
                        name: WithLocation::new(spread.fragment.location, normalization_name),
                        type_: fragment.type_condition,
                        kind: OperationKind::Query,
                        variable_definitions: fragment.variable_definitions.clone(),
                        directives: fragment.directives.clone(),
                        selections: vec![Selection::FragmentSpread(Arc::new(FragmentSpread {
                            arguments: Default::default(),
                            directives: Default::default(),
                            fragment: spread.fragment,
                        }))],
                    },
                )
            });
        created_split_operation
            .0
            .parent_sources
            .insert(spread.fragment.item);

        // @relay_client_component -> @relay_client_component_server(module_id: "...")
        let module_id = get_fragment_filename(spread.fragment.item);
        let mut next_directives = spread.directives.clone();
        if let Some(relay_client_component_directive) = next_directives
            .iter_mut()
            .find(|directive| directive.name.item == *RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME)
        {
            *relay_client_component_directive = Directive {
                name: WithLocation {
                    item: *RELAY_CLIENT_COMPONENT_SERVER_DIRECTIVE_NAME,
                    location: relay_client_component_directive.name.location,
                },
                arguments: vec![Argument {
                    name: WithLocation::generated(*RELAY_CLIENT_COMPONENT_MODULE_ID_ARGUMENT_NAME),
                    value: WithLocation::generated(Value::Constant(ConstantValue::String(
                        module_id,
                    ))),
                }],
            };
        }

        // Record the SplitOperation so we can emit metadata later
        self.split_operation_filenames.insert(module_id);

        Ok(Transformed::Replace(Selection::FragmentSpread(Arc::new(
            FragmentSpread {
                directives: next_directives,
                ..spread.clone()
            },
        ))))
    }

    fn get_incompatible_directives_diagnostic(
        &self,
        spread: &FragmentSpread,
    ) -> Option<Diagnostic> {
        let incompatible_directives = spread
            .directives
            .iter()
            .filter_map(|directive| {
                if !(directive.name.item == *RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME
                    || DIRECTIVE_COMPATIBILITY_ALLOWLIST.contains(&directive.name.item))
                {
                    Some(directive.name.item)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();
        if !incompatible_directives.is_empty() {
            Some(Diagnostic::error(
                ValidationMessage::IncompatibleRelayClientComponentDirectives {
                    incompatible_directives,
                },
                spread.fragment.location,
            ))
        } else {
            None
        }
    }

    fn generate_relay_client_component_client_metadata_directive(&self) -> Directive {
        let mut split_operation_filenames: Vec<StringKey> =
            self.split_operation_filenames.iter().copied().collect();
        split_operation_filenames.sort();
        Directive {
            name: WithLocation::generated(*RELAY_CLIENT_COMPONENT_METADATA_KEY),
            arguments: vec![Argument {
                name: WithLocation::generated(
                    *RELAY_CLIENT_COMPONENT_METADATA_SPLIT_OPERATION_ARG_KEY,
                ),
                value: WithLocation::generated(Value::Constant(ConstantValue::List(
                    split_operation_filenames
                        .into_iter()
                        .map(ConstantValue::String)
                        .collect(),
                ))),
            }],
        }
    }
}

impl<'program> Transformer for RelayClientComponentTransform<'program> {
    const NAME: &'static str = "RelayClientComponentTransform";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        self.split_operation_filenames.clear();
        let transformed = self.default_transform_operation(operation);
        if self.split_operation_filenames.is_empty() {
            return transformed;
        }

        let mut operation = transformed.unwrap_or_else(|| operation.clone());
        operation.directives.reserve_exact(1);
        operation
            .directives
            .push(self.generate_relay_client_component_client_metadata_directive());
        Transformed::Replace(operation)
    }

    fn transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        self.split_operation_filenames.clear();
        let transformed = self.default_transform_fragment(fragment);
        if self.split_operation_filenames.is_empty() {
            return transformed;
        }

        let mut fragment = transformed.unwrap_or_else(|| fragment.clone());
        fragment.directives.reserve_exact(1);
        fragment
            .directives
            .push(self.generate_relay_client_component_client_metadata_directive());
        Transformed::Replace(fragment)
    }

    fn transform_fragment_spread(&mut self, spread: &FragmentSpread) -> Transformed<Selection> {
        let relay_client_component_directive = spread
            .directives
            .named(*RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME);
        if relay_client_component_directive.is_some() {
            match self.transform_relay_client_component(spread) {
                Ok(transformed) => transformed,
                Err(err) => {
                    self.errors.push(err);
                    self.default_transform_fragment_spread(spread)
                }
            }
        } else {
            self.default_transform_fragment_spread(spread)
        }
    }
}
<commit_msg>remove unused DIRECTIVE_COMPATIBILITY_ALLOWLIST<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use super::ValidationMessage;
use crate::match_::SplitOperationMetadata;
use crate::util::{get_fragment_filename, get_normalization_operation_name};
use common::{Diagnostic, DiagnosticsResult, NamedItem, WithLocation};
use fnv::{FnvHashMap, FnvHashSet};
use graphql_ir::{
    Argument, ConstantValue, Directive, FragmentDefinition, FragmentSpread, OperationDefinition,
    Program, Selection, Transformed, Transformer, Value,
};
use graphql_syntax::OperationKind;
use interner::{Intern, StringKey};
use lazy_static::lazy_static;
use schema::{InterfaceID, Schema, Type};
use std::sync::Arc;

lazy_static! {
    pub static ref RELAY_CLIENT_COMPONENT_SERVER_DIRECTIVE_NAME: StringKey =
        "relay_client_component_server".intern();
    pub static ref RELAY_CLIENT_COMPONENT_MODULE_ID_ARGUMENT_NAME: StringKey = "module_id".intern();
    pub static ref RELAY_CLIENT_COMPONENT_METADATA_KEY: StringKey =
        "__RelayClientComponentMetadata".intern();
    pub static ref RELAY_CLIENT_COMPONENT_METADATA_SPLIT_OPERATION_ARG_KEY: StringKey =
        "__RelayClientComponentMetadataSplitOperation".intern();
    static ref RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME: StringKey = "relay_client_component".intern();
    static ref STRING_TYPE: StringKey = "String".intern();
    static ref ID_FIELD_NAME: StringKey = "id".intern();
    static ref NODE_TYPE_NAME: StringKey = "Node".intern();
    static ref VIEWER_TYPE_NAME: StringKey = "Viewer".intern();
}

pub fn relay_client_component(program: &Program) -> DiagnosticsResult<Program> {
    // Noop, the @relay_client_component_server directive is not defined in the schema
    if program
        .schema
        .get_directive(*RELAY_CLIENT_COMPONENT_SERVER_DIRECTIVE_NAME)
        .is_none()
    {
        return Ok(program.clone());
    }
    let node_interface_id = program
        .schema
        .get_type(*NODE_TYPE_NAME)
        .and_then(|type_| {
            if let Type::Interface(id) = type_ {
                Some(id)
            } else {
                None
            }
        })
        .expect("@relay_client_component requires your schema to define the Node interface.");

    let mut transform = RelayClientComponentTransform::new(program, node_interface_id);
    let mut next_program = transform
        .transform_program(program)
        .replace_or_else(|| program.clone());

    if !transform.split_operations.is_empty() {
        for (_, (metadata, mut operation)) in transform.split_operations.drain() {
            operation.directives.push(metadata.to_directive());
            if let Some(prev_operation) = next_program.operation(operation.name.item) {
                transform.errors.push(Diagnostic::error(
                    ValidationMessage::DuplicateRelayClientComponentSplitOperation,
                    prev_operation.name.location,
                ));
            } else {
                next_program.insert_operation(Arc::new(operation))
            }
        }
    }

    if transform.errors.is_empty() {
        Ok(next_program)
    } else {
        Err(transform.errors)
    }
}

struct RelayClientComponentTransform<'program> {
    program: &'program Program,
    errors: Vec<Diagnostic>,
    split_operations: FnvHashMap<StringKey, (SplitOperationMetadata, OperationDefinition)>,
    node_interface_id: InterfaceID,
    split_operation_filenames: FnvHashSet<StringKey>,
}

impl<'program> RelayClientComponentTransform<'program> {
    fn new(program: &'program Program, node_interface_id: InterfaceID) -> Self {
        Self {
            program,
            errors: Default::default(),
            split_operations: Default::default(),
            node_interface_id,
            split_operation_filenames: Default::default(),
        }
    }

    fn transform_relay_client_component(
        &mut self,
        spread: &FragmentSpread,
    ) -> Result<Transformed<Selection>, Diagnostic> {
        if let Some(incompatible_directives_diagnostic) =
            self.get_incompatible_directives_diagnostic(spread)
        {
            return Err(incompatible_directives_diagnostic);
        }
        // @relay_client_component does not take arguments (yet)
        if let Some(argument) = spread.arguments.first() {
            return Err(Diagnostic::error(
                ValidationMessage::InvalidRelayClientComponentWithArguments,
                argument.name.location,
            ));
        }

        let fragment = self
            .program
            .fragment(spread.fragment.item)
            .unwrap_or_else(|| panic!("Expected to find fragment `{}`", spread.fragment.item));
        // Validate that the fragment's type condition MUST implement `Node`.
        let node_interface_id = self.node_interface_id;
        let implements_node = match fragment.type_condition {
            // Fragments can be specified on object types, interfaces, and unions.
            // https://spec.graphql.org/June2018/#sec-Type-Conditions
            Type::Interface(id) => {
                id == node_interface_id
                    || self
                        .program
                        .schema
                        .interface(id)
                        .implementing_objects
                        .iter()
                        .all(|&object_id| {
                            self.program
                                .schema
                                .object(object_id)
                                .interfaces
                                .iter()
                                .any(|interface_id| *interface_id == node_interface_id)
                        })
            }
            Type::Object(id) => self
                .program
                .schema
                .object(id)
                .interfaces
                .iter()
                .any(|interface_id| *interface_id == node_interface_id),
            Type::Union(id) => self
                .program
                .schema
                .union(id)
                .members
                .iter()
                .all(|&object_id| {
                    self.program
                        .schema
                        .object(object_id)
                        .interfaces
                        .iter()
                        .any(|interface_id| *interface_id == node_interface_id)
                }),
            _ => false,
        };
        let is_fragment_on_query =
            fragment.type_condition == self.program.schema.query_type().unwrap();
        let is_fragment_on_viewer =
            self.program.schema.get_type_name(fragment.type_condition) == *VIEWER_TYPE_NAME;
        if !implements_node && !is_fragment_on_query && !is_fragment_on_viewer {
            return Err(Diagnostic::error(
                ValidationMessage::InvalidRelayClientComponentNonNodeFragment,
                fragment.name.location,
            ));
        }

        // Generate a SplitOperation AST
        let normalization_name = get_normalization_operation_name(spread.fragment.item).intern();
        let created_split_operation = self
            .split_operations
            .entry(spread.fragment.item)
            .or_insert_with(|| {
                (
                    SplitOperationMetadata {
                        derived_from: spread.fragment.item,
                        parent_sources: Default::default(),
                        raw_response_type: false,
                    },
                    OperationDefinition {
                        name: WithLocation::new(spread.fragment.location, normalization_name),
                        type_: fragment.type_condition,
                        kind: OperationKind::Query,
                        variable_definitions: fragment.variable_definitions.clone(),
                        directives: fragment.directives.clone(),
                        selections: vec![Selection::FragmentSpread(Arc::new(FragmentSpread {
                            arguments: Default::default(),
                            directives: Default::default(),
                            fragment: spread.fragment,
                        }))],
                    },
                )
            });
        created_split_operation
            .0
            .parent_sources
            .insert(spread.fragment.item);

        // @relay_client_component -> @relay_client_component_server(module_id: "...")
        let module_id = get_fragment_filename(spread.fragment.item);
        let mut next_directives = spread.directives.clone();
        if let Some(relay_client_component_directive) = next_directives
            .iter_mut()
            .find(|directive| directive.name.item == *RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME)
        {
            *relay_client_component_directive = Directive {
                name: WithLocation {
                    item: *RELAY_CLIENT_COMPONENT_SERVER_DIRECTIVE_NAME,
                    location: relay_client_component_directive.name.location,
                },
                arguments: vec![Argument {
                    name: WithLocation::generated(*RELAY_CLIENT_COMPONENT_MODULE_ID_ARGUMENT_NAME),
                    value: WithLocation::generated(Value::Constant(ConstantValue::String(
                        module_id,
                    ))),
                }],
            };
        }

        // Record the SplitOperation so we can emit metadata later
        self.split_operation_filenames.insert(module_id);

        Ok(Transformed::Replace(Selection::FragmentSpread(Arc::new(
            FragmentSpread {
                directives: next_directives,
                ..spread.clone()
            },
        ))))
    }

    fn get_incompatible_directives_diagnostic(
        &self,
        spread: &FragmentSpread,
    ) -> Option<Diagnostic> {
        let incompatible_directives = spread
            .directives
            .iter()
            .filter_map(|directive| {
                if directive.name.item != *RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME {
                    Some(directive.name.item)
                } else {
                    None
                }
            })
            .collect::<Vec<StringKey>>();
        if !incompatible_directives.is_empty() {
            Some(Diagnostic::error(
                ValidationMessage::IncompatibleRelayClientComponentDirectives {
                    incompatible_directives,
                },
                spread.fragment.location,
            ))
        } else {
            None
        }
    }

    fn generate_relay_client_component_client_metadata_directive(&self) -> Directive {
        let mut split_operation_filenames: Vec<StringKey> =
            self.split_operation_filenames.iter().copied().collect();
        split_operation_filenames.sort();
        Directive {
            name: WithLocation::generated(*RELAY_CLIENT_COMPONENT_METADATA_KEY),
            arguments: vec![Argument {
                name: WithLocation::generated(
                    *RELAY_CLIENT_COMPONENT_METADATA_SPLIT_OPERATION_ARG_KEY,
                ),
                value: WithLocation::generated(Value::Constant(ConstantValue::List(
                    split_operation_filenames
                        .into_iter()
                        .map(ConstantValue::String)
                        .collect(),
                ))),
            }],
        }
    }
}

impl<'program> Transformer for RelayClientComponentTransform<'program> {
    const NAME: &'static str = "RelayClientComponentTransform";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        self.split_operation_filenames.clear();
        let transformed = self.default_transform_operation(operation);
        if self.split_operation_filenames.is_empty() {
            return transformed;
        }

        let mut operation = transformed.unwrap_or_else(|| operation.clone());
        operation.directives.reserve_exact(1);
        operation
            .directives
            .push(self.generate_relay_client_component_client_metadata_directive());
        Transformed::Replace(operation)
    }

    fn transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        self.split_operation_filenames.clear();
        let transformed = self.default_transform_fragment(fragment);
        if self.split_operation_filenames.is_empty() {
            return transformed;
        }

        let mut fragment = transformed.unwrap_or_else(|| fragment.clone());
        fragment.directives.reserve_exact(1);
        fragment
            .directives
            .push(self.generate_relay_client_component_client_metadata_directive());
        Transformed::Replace(fragment)
    }

    fn transform_fragment_spread(&mut self, spread: &FragmentSpread) -> Transformed<Selection> {
        let relay_client_component_directive = spread
            .directives
            .named(*RELAY_CLIENT_COMPONENT_DIRECTIVE_NAME);
        if relay_client_component_directive.is_some() {
            match self.transform_relay_client_component(spread) {
                Ok(transformed) => transformed,
                Err(err) => {
                    self.errors.push(err);
                    self.default_transform_fragment_spread(spread)
                }
            }
        } else {
            self.default_transform_fragment_spread(spread)
        }
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate clap;
#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
extern crate mprovision;
#[macro_use]
extern crate structopt;

use chrono::*;
use cli::Command;
use mprovision as mp;
use std::env;
use std::path::PathBuf;

mod cli;

fn main() {
    if let Err(e) = cli::parse(env::args()).and_then(run) {
        e.exit();
    }
}

fn run(command: cli::Command) -> Result<(), cli::Error> {
    match command {
        Command::List(cli::ListParams {
            text,
            expire_in_days,
            directory,
        }) => list(text, expire_in_days, directory),
        Command::ShowUuid(cli::ShowUuidParams { uuid, directory }) => show_uuid(uuid, directory),
        Command::ShowFile(cli::ShowFileParams { file }) => show_file(file),
        Command::Remove(cli::RemoveParams { ids, directory }) => remove(ids, directory),
        Command::Clean(cli::CleanParams { directory }) => clean(directory),
    }
}

fn list(
    text: Option<String>,
    expires_in_days: Option<i64>,
    directory: Option<PathBuf>,
) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let total = entries.len();
            let date = expires_in_days.map(|days| Utc::now() + Duration::days(days));
            let filter_string = text.as_ref();
            let mut profiles = mp::filter(entries, |profile| match (date, filter_string) {
                (Some(date), Some(string)) => {
                    profile.info.expiration_date <= date && profile.info.contains(string)
                }
                (Some(date), _) => profile.info.expiration_date <= date,
                (_, Some(string)) => profile.info.contains(string),
                (_, _) => true,
            });
            profiles.sort_by(|a, b| a.info.creation_date.cmp(&b.info.creation_date));
            (total, profiles)
        })
        .and_then(|(total, profiles)| {
            if profiles.is_empty() {
                Ok(println!("Nothing found"))
            } else {
                for profile in &profiles {
                    println!("\n{}", profile.info.description());
                }
                Ok(println!("\nFound {} of {}", profiles.len(), total))
            }
        })
}

fn show_uuid(uuid: String, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_uuid(&directory, &uuid)
                .and_then(|profile| mp::show(&profile.path).map(|xml| println!("{}", xml)))
        })
        .map_err(|err| err.into())
}

fn show_file(path: PathBuf) -> Result<(), cli::Error> {
    mp::show(&path)
        .map(|xml| println!("{}", xml))
        .map_err(|err| err.into())
}

fn remove(ids: Vec<String>, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_ids(&directory, ids).and_then(|profiles| {
                for profile in profiles {
                    match mp::remove(&profile.path) {
                        Ok(_) => println!("'{}' was removed", profile.info.uuid),
                        Err(_) => println!("Error while removing '{}'", profile.info.uuid),
                    }
                }
                Ok(())
            })
        })
        .map_err(|err| err.into())
}

fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {
    fn concat(mut s1: String, s2: String) -> String {
        s1.push_str(&s2);
        s1
    }

    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let date = Utc::now();
            mp::filter(entries, |profile| profile.info.expiration_date <= date)
        })
        .and_then(|profiles| {
            if profiles.is_empty() {
                Ok(println!("All provisioning profiles are valid"))
            } else {
                profiles
                    .iter()
                    .map(|profile| {
                        std::fs::remove_file(&profile.path)
                            .map(|_| format!("'{}' was removed\n", profile.info.uuid))
                            .map_err(|err| format!("'{}' {}\n", profile.info.uuid, err))
                    })
                    .fold(Ok(String::new()), |acc, s| match (acc, s) {
                        (Ok(acc), Ok(s)) => Ok(concat(acc, s)),
                        (Ok(acc), Err(s)) | (Err(acc), Ok(s)) | (Err(acc), Err(s)) => {
                            Err(concat(acc, s))
                        }
                    })
                    .map(|s| println!("{}", s))
                    .map_err(cli::Error::Custom)
            }
        })
}
<commit_msg>Print full info when removing profile, Closes #15<commit_after>extern crate chrono;
extern crate clap;
#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
extern crate mprovision;
#[macro_use]
extern crate structopt;

use chrono::*;
use cli::Command;
use mprovision as mp;
use std::env;
use std::path::PathBuf;

mod cli;

fn main() {
    if let Err(e) = cli::parse(env::args()).and_then(run) {
        e.exit();
    }
}

fn run(command: cli::Command) -> Result<(), cli::Error> {
    match command {
        Command::List(cli::ListParams {
            text,
            expire_in_days,
            directory,
        }) => list(text, expire_in_days, directory),
        Command::ShowUuid(cli::ShowUuidParams { uuid, directory }) => show_uuid(uuid, directory),
        Command::ShowFile(cli::ShowFileParams { file }) => show_file(file),
        Command::Remove(cli::RemoveParams { ids, directory }) => remove(ids, directory),
        Command::Clean(cli::CleanParams { directory }) => clean(directory),
    }
}

fn list(
    text: Option<String>,
    expires_in_days: Option<i64>,
    directory: Option<PathBuf>,
) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let total = entries.len();
            let date = expires_in_days.map(|days| Utc::now() + Duration::days(days));
            let filter_string = text.as_ref();
            let mut profiles = mp::filter(entries, |profile| match (date, filter_string) {
                (Some(date), Some(string)) => {
                    profile.info.expiration_date <= date && profile.info.contains(string)
                }
                (Some(date), _) => profile.info.expiration_date <= date,
                (_, Some(string)) => profile.info.contains(string),
                (_, _) => true,
            });
            profiles.sort_by(|a, b| a.info.creation_date.cmp(&b.info.creation_date));
            (total, profiles)
        })
        .and_then(|(total, profiles)| {
            if profiles.is_empty() {
                Ok(println!("Nothing found"))
            } else {
                for profile in &profiles {
                    println!("\n{}", profile.info.description());
                }
                Ok(println!("\nFound {} of {}", profiles.len(), total))
            }
        })
}

fn show_uuid(uuid: String, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_uuid(&directory, &uuid)
                .and_then(|profile| mp::show(&profile.path).map(|xml| println!("{}", xml)))
        })
        .map_err(|err| err.into())
}

fn show_file(path: PathBuf) -> Result<(), cli::Error> {
    mp::show(&path)
        .map(|xml| println!("{}", xml))
        .map_err(|err| err.into())
}

fn remove(ids: Vec<String>, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_ids(&directory, ids).and_then(|profiles| {
                for profile in profiles {
                    match mp::remove(&profile.path) {
                        Ok(_) => println!("\nRemoved: {}", profile.info.description()),
                        Err(_) => println!("\nError while removing '{}'", profile.info.uuid),
                    }
                }
                Ok(())
            })
        })
        .map_err(|err| err.into())
}

fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {
    fn concat(mut s1: String, s2: String) -> String {
        s1.push_str(&s2);
        s1
    }

    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| {
            let date = Utc::now();
            mp::filter(entries, |profile| profile.info.expiration_date <= date)
        })
        .and_then(|profiles| {
            if profiles.is_empty() {
                Ok(println!("All provisioning profiles are valid"))
            } else {
                profiles
                    .iter()
                    .map(|profile| {
                        std::fs::remove_file(&profile.path)
                            .map(|_| format!("'{}' was removed\n", profile.info.uuid))
                            .map_err(|err| format!("'{}' {}\n", profile.info.uuid, err))
                    })
                    .fold(Ok(String::new()), |acc, s| match (acc, s) {
                        (Ok(acc), Ok(s)) => Ok(concat(acc, s)),
                        (Ok(acc), Err(s)) | (Err(acc), Ok(s)) | (Err(acc), Err(s)) => {
                            Err(concat(acc, s))
                        }
                    })
                    .map(|s| println!("{}", s))
                    .map_err(cli::Error::Custom)
            }
        })
}
<|endoftext|>


<commit_before>struct Fibonacci {
    current: u32,
    next: u32,
}

impl Iterator for Fibonacci {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        let temp = self.current +self.next;
        self.current = self.next;
        self.next = temp;
        Some(self.current)
    }
}

fn fibonacci() -> Fibonacci {
    Fibonacci {
        current: 1,
        next: 1,
    }
}

fn main() {
    //TODO print sum
    //let sum = fibonacci()
    //    .filter(|f| f % 2 == 0) // even elements
    //    .take_while(|n| n <= &bla) // below 4 mil
    //    .sum();
    println!("The sum of all even Fibonacci elements with value below 4.000.000 equals to: {}", sum);
}
<commit_msg>problem 2 working correctly<commit_after>struct Fibonacci {
    current: u32,
    next: u32,
}

impl Iterator for Fibonacci {
    type Item = u32;
    fn next(&mut self) -> Option<u32> {
        let temp = self.current + self.next;
        self.current = self.next;
        self.next = temp;

        // Some is always returned
        Some(self.current)
    }
}

// sequence generator
fn fibonacci() -> Fibonacci {
    Fibonacci {
        current: 1,
        next: 1,
    }
}

fn main() {
    let mut sum:u32 = 0;
    for i in fibonacci().filter(|&x| x % 2 == 0).take_while(|&x| x < 4_000_000) {
        sum += i;
    }

    //let test  = fibonacci()
    //    .filter(|f| f % 2 == 0) // even elements
    //    .take_while(|&v| v <= 4_000_000u32);
    println!("The sum of all even Fibonacci elements with value below 4.000.000 equals to: {}", sum);
}
<|endoftext|>


<commit_before>//! Data structures that represent module hierarchy and dependencies.
use arrayvec::ArrayString;
use petgraph::graphmap::DiGraphMap;
use std::cmp::{Ord, Ordering};
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::ops::Add;

/// Determines the maximum length of the #cfg that
/// can be attached to a mod definition.
const CONDITIONS_SIZE: usize = 100;

/// Determines the maximum length of a module's path.
///
/// eg: `"my_crate::foo::bar::baz"`.
const MOD_PATH_SIZE: usize = 200;

const SELF_KEYWORD: &str = "self";

pub const SEP: &str = "::";

pub type Graph = DiGraphMap<Module, Edge>;

/// Represents the scope of dependencies between modules.
///
/// See also [`Edge`].
#[derive(Clone, Debug, PartialEq)]
pub struct Dependency {
    /// Eg: `use path::to::mod::*;`
    refers_to_all: bool,
    /// Eg: `use path::to::mod;` or `use path::to::mod::{self, ...}`
    refers_to_mod: bool,
    /// Eg: `use path::to::mod::{some_function, SomeStruct};`
    referred_members: HashSet<String>,
}

impl Dependency {
    pub fn module() -> Self {
        Self {
            refers_to_all: false,
            refers_to_mod: true,
            referred_members: HashSet::new(),
        }
    }

    pub fn all() -> Self {
        Self {
            refers_to_all: true,
            refers_to_mod: false,
            referred_members: HashSet::new(),
        }
    }

    pub fn members(mut members: Vec<String>) -> Self {
        let refers_to_mod: bool = members.contains(&String::from(SELF_KEYWORD));
        let referred_members: HashSet<String> =
            members.drain(..).filter(|m| m != SELF_KEYWORD).collect();
        Self {
            refers_to_all: false,
            refers_to_mod,
            referred_members,
        }
    }
}

impl Add for Dependency {
    type Output = Dependency;

    fn add(self, other: Dependency) -> Dependency {
        Dependency {
            refers_to_all: self.refers_to_all || other.refers_to_all,
            refers_to_mod: self.refers_to_mod || other.refers_to_mod,
            referred_members: self
                .referred_members
                .union(&other.referred_members)
                .cloned()
                .collect(),
        }
    }
}

/// Represents an association between modules.
///
/// This enum is intended to be used with directed graphs because
/// relationships represented are asymmetric.
#[derive(Clone, Debug, PartialEq)]
pub enum Edge {
    /// Represents a **parent ⇒ child** relationship.
    Child,
    /// Represents a **dependent ⇒ dependency** relationship created by `use`.
    Dependency(Dependency),
    /// Like `Child` but the **child** module is an orphan.  So this
    /// represents a _lack of_ semantic relationship.
    Unconnected,
}

/// Builds a graph, `DiGraphMap<Mod, Edge>` to be specific using domain
/// specific operations.
pub struct GraphBuilder {
    graph: Graph,
    uses: HashMap<String, HashSet<String>>,
}

impl GraphBuilder {
    pub fn new() -> Self {
        Self {
            graph: DiGraphMap::new(),
            uses: HashMap::new(),
        }
    }

    /// Define a public module that has no parent.
    ///
    /// # Panics
    /// If `name` contains `"::"`.
    pub fn add_crate_root(&mut self, name: &str) {
        assert!(!name.contains(SEP));
        self.graph.add_node(Module::new_root(name));
    }

    /// Define a sub-modules and associate it with its parent.
    ///
    /// # Panics
    /// - If the parent is not already defined.
    /// - If parent-child relationship for this pair is already defined.
    pub fn add_mod(
        &mut self,
        path: &str,
        name: &str,
        visibility: Visibility,
        conditions: Option<&str>,
    ) {
        let parent: Module = self.find(path).unwrap();
        let node = Module::new(&[path, SEP, name].concat(), Some(visibility), conditions);
        self.graph.add_node(node);
        assert!(self.graph.add_edge(parent, node, Edge::Child).is_none());
    }

    pub fn add_orphan(&mut self, path: &str, name: &str) {
        let parent: Module = self.find(path).unwrap();
        let node = Module::new(&[path, SEP, name].concat(), None, None);
        self.graph.add_node(node);
        assert!(self
            .graph
            .add_edge(parent, node, Edge::Unconnected)
            .is_none());
    }

    pub fn add_use(&mut self, path: &str, use_: &str) {
        if !self.uses.contains_key(path) {
            self.uses.insert(path.to_owned(), HashSet::new());
        }
        self.uses.get_mut(path).unwrap().insert(use_.to_owned());
    }

    /// Build the graph, consuming this builder, or return an error.
    pub fn build(mut self) -> Result<Graph, GraphError> {
        let mut result: Result<(), GraphError> = Ok(());
        for (from, uses) in &self.uses {
            for to in uses {
                match (self.find(&from), self.find(&to)) {
                    (Some(from), Some(to)) => {
                        assert!(self
                            .graph
                            .add_edge(from, to, Edge::Dependency(Dependency::module()))
                            .is_none());
                    }
                    (None, _) => panic!("Trying to add dependency from an unknown module"),
                    (_, None) => {
                        result = Err(GraphError::UnknownModule(to.to_owned()));
                        break;
                    }
                };
            }
        }
        result.map(|_| self.graph)
    }

    pub fn find(&self, path: &str) -> Option<Module> {
        self.graph.nodes().find(|m| m.path() == path)
    }
}

#[derive(Debug, PartialEq)]
pub enum GraphError {
    UnknownModule(String),
}

/// Represents a node that is a module in the graph.
#[derive(Clone, Copy, Debug)]
pub struct Module {
    is_root: bool,
    /// Because this struct needs to be `Copy`, using `String` or `&str` was
    /// out of the question.  `ArrayString` provides a sized and owned string
    /// that is backed by a byte array.
    ///
    /// See also [MOD_PATH_SIZE]
    path: ArrayString<[u8; MOD_PATH_SIZE]>,
    name_ridx: usize,
    visibility: Option<Visibility>,
    /// This needs to be `Copy` for the same reason as `path`.
    conditions: Option<ArrayString<[u8; CONDITIONS_SIZE]>>,
}

impl Module {
    pub fn new(path: &str, visibility: Option<Visibility>, conditions: Option<&str>) -> Self {
        Self {
            is_root: false,
            path: ArrayString::<[u8; MOD_PATH_SIZE]>::from(path)
                .unwrap_or_else(|_| panic!("Module path is too long")),
            name_ridx: path.rfind(SEP).map(|i| i + SEP.len()).unwrap_or(0),
            visibility,
            conditions: conditions.map(|c| {
                ArrayString::<[u8; CONDITIONS_SIZE]>::from(c)
                    .unwrap_or_else(|_| panic!("Conditions are too long"))
            }),
        }
    }

    pub fn new_root(path: &str) -> Self {
        let mut m = Self::new(path, Some(Visibility::Public), None);
        m.is_root = true;
        m
    }

    pub fn conditions(&self) -> Option<&str> {
        self.conditions.as_ref().map(|c| &c[..])
    }

    pub fn name(&self) -> &str {
        &self.path[self.name_ridx..]
    }

    pub fn is_orphan(&self) -> bool {
        self.visibility.is_none()
    }

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

    pub fn path(&self) -> &str {
        &self.path.as_str()
    }

    pub fn visibility(&self) -> Option<Visibility> {
        self.visibility
    }
}

impl Eq for Module {}

impl Hash for Module {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.path().hash(state);
    }
}

impl Ord for Module {
    fn cmp(&self, other: &Self) -> Ordering {
        self.path().cmp(&other.path())
    }
}

impl PartialEq for Module {
    fn eq(&self, other: &Self) -> bool {
        self.path() == other.path()
    }
}

impl PartialOrd for Module {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

// TODO: We should support more kinds of visibility.
//
// Defined kinds are:
//
// - Public
// - Crate
// - Restricted
// - Inherited
//
// See: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/enum.VisibilityKind.html
#[derive(Clone, Copy, Debug)]
pub enum Visibility {
    Public,
    Private,
}

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

    #[test]
    fn new_builder_produces_an_empty_directed_graph() {
        let builder = GraphBuilder::new();
        let graph: Graph = builder.build().unwrap();
        assert_eq!(0, graph.node_count());
        assert_eq!(0, graph.edge_count());
    }

    #[test]
    fn add_crate_root_adds_a_node() {
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("crate-root");
        let graph: Graph = builder.build().unwrap();
        assert_eq!(1, graph.node_count());
        assert_eq!(0, graph.edge_count());
        assert!(graph.contains_node(Module::new("crate-root", Some(Visibility::Public), None)));
    }

    #[test]
    fn add_mod_creates_an_association_with_parent() {
        let foo: Module = Module::new("foo", Some(Visibility::Public), None);
        let bar: Module = Module::new("foo::bar", Some(Visibility::Public), None);
        let baz: Module = Module::new("foo::bar::baz", Some(Visibility::Private), None);
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("foo");
        builder.add_mod("foo", "bar", Visibility::Public, None);
        builder.add_mod("foo::bar", "baz", Visibility::Private, None);
        let graph: Graph = builder.build().unwrap();
        assert_eq!(3, graph.node_count());
        assert_eq!(2, graph.edge_count());
        assert!(graph.contains_node(foo));
        assert!(graph.contains_node(bar));
        assert!(graph.contains_node(baz));
        assert_eq!(
            vec![(foo, bar, &Edge::Child), (bar, baz, &Edge::Child)],
            graph.all_edges().collect::<Vec<(Module, Module, &Edge)>>()
        );
    }

    #[test]
    #[should_panic(expected = "Module path is too long")]
    fn add_mod_panics_if_path_is_longer_than_limit() {
        let name = "foo";
        let path = std::str::from_utf8([114; MOD_PATH_SIZE - 4].as_ref()).unwrap(); // 'r'
        let concatenated = format!("{}::{}", path, name);
        assert!(concatenated.len() > MOD_PATH_SIZE);
        let mut builder = GraphBuilder::new();
        builder.add_crate_root(path);
        builder.add_mod(path, name, Visibility::Public, None);
    }

    #[test]
    fn adding_a_use_is_idempotent() {
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("foo");
        builder.add_mod("foo", "bar", Visibility::Public, None);
        builder.add_mod("foo", "baz", Visibility::Private, None);
        builder.add_use("foo::bar", "foo::baz");
        builder.add_use("foo::bar", "foo::baz");
        let graph = builder.build().unwrap();
        assert_eq!(3, graph.edge_count());
    }

    #[test]
    fn add_dep_requires_both_modules_to_be_defined() {
        {
            let mut builder = GraphBuilder::new();
            builder.add_crate_root("foo");
            builder.add_mod("foo", "bar", Visibility::Public, None);
            builder.add_use("foo::bar", "foo::baz");
            assert_eq!(
                Some(GraphError::UnknownModule(String::from("foo::baz"))),
                builder.build().err()
            );
        }
        {
            let mut builder = GraphBuilder::new();
            builder.add_crate_root("foo");
            builder.add_mod("foo", "bar", Visibility::Private, None);
            builder.add_use("foo::bar", "foo::baz");
            builder.add_use("foo::bar", "foo::fubar");
            builder.add_mod("foo", "baz", Visibility::Private, None);
            assert_eq!(
                Some(GraphError::UnknownModule(String::from("foo::fubar"))),
                builder.build().err()
            );
        }
    }

    #[test]
    #[should_panic(expected = "Trying to add dependency from an unknown module")]
    #[allow(unused_must_use)]
    fn dependency_from_unknown_module_panics() {
        {
            let mut builder = GraphBuilder::new();
            builder.add_crate_root("foo");
            builder.add_use("foo::bar", "foo::baz");
            builder.build();
        }
    }

    #[test]
    fn orphaned_modules_are_linked_to_their_parent_via_unconnected_edges() {
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("foo");
        builder.add_mod("foo", "bar", Visibility::Public, None);
        builder.add_orphan("foo", "baz");
        builder.add_orphan("foo::bar", "bat");
        let graph = builder.build().unwrap();
        assert_eq!(3, graph.edge_count());

        // Check edge `foo -> baz`
        assert!(graph.contains_edge(
            Module::new_root("foo"),
            Module::new("foo::bar", Some(Visibility::Public), None)
        ));
        assert_eq!(
            Some(&Edge::Unconnected),
            graph.edge_weight(
                Module::new_root("foo"),
                Module::new("foo::baz", Some(Visibility::Public), None)
            )
        );

        // Check edge `bar -> bat`
        assert!(graph.contains_edge(
            Module::new("foo::bar", Some(Visibility::Public), None),
            Module::new("foo::bar::bat", Some(Visibility::Public), None)
        ));
        assert_eq!(
            Some(&Edge::Unconnected),
            graph.edge_weight(
                Module::new("foo::bar", Some(Visibility::Public), None),
                Module::new("foo::bar::bat", Some(Visibility::Public), None)
            )
        );
    }

    #[test]
    fn module_name() {
        assert_eq!(Module::new_root("foo").name(), "foo");
        assert_eq!(
            Module::new("foo::bar", Some(Visibility::Public), None).name(),
            "bar"
        );
        assert_eq!(
            Module::new("foo::bar::baz::bat::quux", Some(Visibility::Private), None).name(),
            "quux"
        );
    }
}
<commit_msg>Extract method for dependency edge generation<commit_after>//! Data structures that represent module hierarchy and dependencies.
use arrayvec::ArrayString;
use petgraph::graphmap::DiGraphMap;
use std::cmp::{Ord, Ordering};
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::ops::Add;

/// Determines the maximum length of the #cfg that
/// can be attached to a mod definition.
const CONDITIONS_SIZE: usize = 100;

/// Determines the maximum length of a module's path.
///
/// eg: `"my_crate::foo::bar::baz"`.
const MOD_PATH_SIZE: usize = 200;

const SELF_KEYWORD: &str = "self";

pub const SEP: &str = "::";

pub type Graph = DiGraphMap<Module, Edge>;

/// Represents the scope of dependencies between modules.
///
/// See also [`Edge`].
#[derive(Clone, Debug, PartialEq)]
pub struct Dependency {
    /// Eg: `use path::to::mod::*;`
    refers_to_all: bool,
    /// Eg: `use path::to::mod;` or `use path::to::mod::{self, ...}`
    refers_to_mod: bool,
    /// Eg: `use path::to::mod::{some_function, SomeStruct};`
    referred_members: HashSet<String>,
}

impl Dependency {
    pub fn module() -> Self {
        Self {
            refers_to_all: false,
            refers_to_mod: true,
            referred_members: HashSet::new(),
        }
    }

    pub fn all() -> Self {
        Self {
            refers_to_all: true,
            refers_to_mod: false,
            referred_members: HashSet::new(),
        }
    }

    pub fn members(mut members: Vec<String>) -> Self {
        let refers_to_mod: bool = members.contains(&String::from(SELF_KEYWORD));
        let referred_members: HashSet<String> =
            members.drain(..).filter(|m| m != SELF_KEYWORD).collect();
        Self {
            refers_to_all: false,
            refers_to_mod,
            referred_members,
        }
    }
}

impl Add for Dependency {
    type Output = Dependency;

    fn add(self, other: Dependency) -> Dependency {
        Dependency {
            refers_to_all: self.refers_to_all || other.refers_to_all,
            refers_to_mod: self.refers_to_mod || other.refers_to_mod,
            referred_members: self
                .referred_members
                .union(&other.referred_members)
                .cloned()
                .collect(),
        }
    }
}

/// Represents an association between modules.
///
/// This enum is intended to be used with directed graphs because
/// relationships represented are asymmetric.
#[derive(Clone, Debug, PartialEq)]
pub enum Edge {
    /// Represents a **parent ⇒ child** relationship.
    Child,
    /// Represents a **dependent ⇒ dependency** relationship created by `use`.
    Dependency(Dependency),
    /// Like `Child` but the **child** module is an orphan.  So this
    /// represents a _lack of_ semantic relationship.
    Unconnected,
}

/// Builds a graph, `DiGraphMap<Mod, Edge>` to be specific using domain
/// specific operations.
pub struct GraphBuilder {
    graph: Graph,
    uses: HashMap<String, HashSet<String>>,
}

impl GraphBuilder {
    pub fn new() -> Self {
        Self {
            graph: DiGraphMap::new(),
            uses: HashMap::new(),
        }
    }

    /// Define a public module that has no parent.
    ///
    /// # Panics
    /// If `name` contains `"::"`.
    pub fn add_crate_root(&mut self, name: &str) {
        assert!(!name.contains(SEP));
        self.graph.add_node(Module::new_root(name));
    }

    /// Define a sub-modules and associate it with its parent.
    ///
    /// # Panics
    /// - If the parent is not already defined.
    /// - If parent-child relationship for this pair is already defined.
    pub fn add_mod(
        &mut self,
        path: &str,
        name: &str,
        visibility: Visibility,
        conditions: Option<&str>,
    ) {
        let parent: Module = self.find(path).unwrap();
        let node = Module::new(&[path, SEP, name].concat(), Some(visibility), conditions);
        self.graph.add_node(node);
        assert!(self.graph.add_edge(parent, node, Edge::Child).is_none());
    }

    pub fn add_orphan(&mut self, path: &str, name: &str) {
        let parent: Module = self.find(path).unwrap();
        let node = Module::new(&[path, SEP, name].concat(), None, None);
        self.graph.add_node(node);
        assert!(self
            .graph
            .add_edge(parent, node, Edge::Unconnected)
            .is_none());
    }

    pub fn add_use(&mut self, path: &str, use_: &str) {
        if !self.uses.contains_key(path) {
            self.uses.insert(path.to_owned(), HashSet::new());
        }
        self.uses.get_mut(path).unwrap().insert(use_.to_owned());
    }

    /// Build the graph, consuming this builder, or return an error.
    pub fn build(mut self) -> Result<Graph, GraphError> {
        // Take ownership of uses separately so that we can still
        // call the builder struct as mutable.
        let uses: HashMap<String, HashSet<String>> =
            std::mem::replace(&mut self.uses, HashMap::new());
        let mut result: Result<(), GraphError> = Ok(());
        for (from, uses) in uses {
            let from_module = self
                .find(&from)
                .expect("Trying to add dependency from an unknown module");
            for to in uses {
                result = self.add_dependency(from_module, &to);
                if result.is_err() {
                    break;
                }
            }
        }
        result.map(|_| self.graph)
    }

    pub fn find(&self, path: &str) -> Option<Module> {
        self.graph.nodes().find(|m| m.path() == path)
    }

    fn add_dependency(&mut self, from: Module, use_: &str) -> Result<(), GraphError> {
        match self.find(use_) {
            Some(to) => {
                // TODO: Don't bash on existing dependency.
                assert!(self
                    .graph
                    .add_edge(from, to, Edge::Dependency(Dependency::module()))
                    .is_none());
                Ok(())
            }
            None => Err(GraphError::UnknownModule(use_.to_owned())),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum GraphError {
    UnknownModule(String),
}

/// Represents a node that is a module in the graph.
#[derive(Clone, Copy, Debug)]
pub struct Module {
    is_root: bool,
    /// Because this struct needs to be `Copy`, using `String` or `&str` was
    /// out of the question.  `ArrayString` provides a sized and owned string
    /// that is backed by a byte array.
    ///
    /// See also [MOD_PATH_SIZE]
    path: ArrayString<[u8; MOD_PATH_SIZE]>,
    name_ridx: usize,
    visibility: Option<Visibility>,
    /// This needs to be `Copy` for the same reason as `path`.
    conditions: Option<ArrayString<[u8; CONDITIONS_SIZE]>>,
}

impl Module {
    pub fn new(path: &str, visibility: Option<Visibility>, conditions: Option<&str>) -> Self {
        Self {
            is_root: false,
            path: ArrayString::<[u8; MOD_PATH_SIZE]>::from(path)
                .unwrap_or_else(|_| panic!("Module path is too long")),
            name_ridx: path.rfind(SEP).map(|i| i + SEP.len()).unwrap_or(0),
            visibility,
            conditions: conditions.map(|c| {
                ArrayString::<[u8; CONDITIONS_SIZE]>::from(c)
                    .unwrap_or_else(|_| panic!("Conditions are too long"))
            }),
        }
    }

    pub fn new_root(path: &str) -> Self {
        let mut m = Self::new(path, Some(Visibility::Public), None);
        m.is_root = true;
        m
    }

    pub fn conditions(&self) -> Option<&str> {
        self.conditions.as_ref().map(|c| &c[..])
    }

    pub fn name(&self) -> &str {
        &self.path[self.name_ridx..]
    }

    pub fn is_orphan(&self) -> bool {
        self.visibility.is_none()
    }

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

    pub fn path(&self) -> &str {
        &self.path.as_str()
    }

    pub fn visibility(&self) -> Option<Visibility> {
        self.visibility
    }
}

impl Eq for Module {}

impl Hash for Module {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.path().hash(state);
    }
}

impl Ord for Module {
    fn cmp(&self, other: &Self) -> Ordering {
        self.path().cmp(&other.path())
    }
}

impl PartialEq for Module {
    fn eq(&self, other: &Self) -> bool {
        self.path() == other.path()
    }
}

impl PartialOrd for Module {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

// TODO: We should support more kinds of visibility.
//
// Defined kinds are:
//
// - Public
// - Crate
// - Restricted
// - Inherited
//
// See: https://doc.rust-lang.org/nightly/nightly-rustc/syntax/ast/enum.VisibilityKind.html
#[derive(Clone, Copy, Debug)]
pub enum Visibility {
    Public,
    Private,
}

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

    #[test]
    fn new_builder_produces_an_empty_directed_graph() {
        let builder = GraphBuilder::new();
        let graph: Graph = builder.build().unwrap();
        assert_eq!(0, graph.node_count());
        assert_eq!(0, graph.edge_count());
    }

    #[test]
    fn add_crate_root_adds_a_node() {
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("crate-root");
        let graph: Graph = builder.build().unwrap();
        assert_eq!(1, graph.node_count());
        assert_eq!(0, graph.edge_count());
        assert!(graph.contains_node(Module::new("crate-root", Some(Visibility::Public), None)));
    }

    #[test]
    fn add_mod_creates_an_association_with_parent() {
        let foo: Module = Module::new("foo", Some(Visibility::Public), None);
        let bar: Module = Module::new("foo::bar", Some(Visibility::Public), None);
        let baz: Module = Module::new("foo::bar::baz", Some(Visibility::Private), None);
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("foo");
        builder.add_mod("foo", "bar", Visibility::Public, None);
        builder.add_mod("foo::bar", "baz", Visibility::Private, None);
        let graph: Graph = builder.build().unwrap();
        assert_eq!(3, graph.node_count());
        assert_eq!(2, graph.edge_count());
        assert!(graph.contains_node(foo));
        assert!(graph.contains_node(bar));
        assert!(graph.contains_node(baz));
        assert_eq!(
            vec![(foo, bar, &Edge::Child), (bar, baz, &Edge::Child)],
            graph.all_edges().collect::<Vec<(Module, Module, &Edge)>>()
        );
    }

    #[test]
    #[should_panic(expected = "Module path is too long")]
    fn add_mod_panics_if_path_is_longer_than_limit() {
        let name = "foo";
        let path = std::str::from_utf8([114; MOD_PATH_SIZE - 4].as_ref()).unwrap(); // 'r'
        let concatenated = format!("{}::{}", path, name);
        assert!(concatenated.len() > MOD_PATH_SIZE);
        let mut builder = GraphBuilder::new();
        builder.add_crate_root(path);
        builder.add_mod(path, name, Visibility::Public, None);
    }

    #[test]
    fn adding_a_use_is_idempotent() {
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("foo");
        builder.add_mod("foo", "bar", Visibility::Public, None);
        builder.add_mod("foo", "baz", Visibility::Private, None);
        builder.add_use("foo::bar", "foo::baz");
        builder.add_use("foo::bar", "foo::baz");
        let graph = builder.build().unwrap();
        assert_eq!(3, graph.edge_count());
    }

    #[test]
    fn add_dep_requires_both_modules_to_be_defined() {
        {
            let mut builder = GraphBuilder::new();
            builder.add_crate_root("foo");
            builder.add_mod("foo", "bar", Visibility::Public, None);
            builder.add_use("foo::bar", "foo::baz");
            assert_eq!(
                Some(GraphError::UnknownModule(String::from("foo::baz"))),
                builder.build().err()
            );
        }
        {
            let mut builder = GraphBuilder::new();
            builder.add_crate_root("foo");
            builder.add_mod("foo", "bar", Visibility::Private, None);
            builder.add_use("foo::bar", "foo::baz");
            builder.add_use("foo::bar", "foo::fubar");
            builder.add_mod("foo", "baz", Visibility::Private, None);
            assert_eq!(
                Some(GraphError::UnknownModule(String::from("foo::fubar"))),
                builder.build().err()
            );
        }
    }

    #[test]
    #[should_panic(expected = "Trying to add dependency from an unknown module")]
    #[allow(unused_must_use)]
    fn dependency_from_unknown_module_panics() {
        {
            let mut builder = GraphBuilder::new();
            builder.add_crate_root("foo");
            builder.add_use("foo::bar", "foo::baz");
            builder.build();
        }
    }

    #[test]
    fn orphaned_modules_are_linked_to_their_parent_via_unconnected_edges() {
        let mut builder = GraphBuilder::new();
        builder.add_crate_root("foo");
        builder.add_mod("foo", "bar", Visibility::Public, None);
        builder.add_orphan("foo", "baz");
        builder.add_orphan("foo::bar", "bat");
        let graph = builder.build().unwrap();
        assert_eq!(3, graph.edge_count());

        // Check edge `foo -> baz`
        assert!(graph.contains_edge(
            Module::new_root("foo"),
            Module::new("foo::bar", Some(Visibility::Public), None)
        ));
        assert_eq!(
            Some(&Edge::Unconnected),
            graph.edge_weight(
                Module::new_root("foo"),
                Module::new("foo::baz", Some(Visibility::Public), None)
            )
        );

        // Check edge `bar -> bat`
        assert!(graph.contains_edge(
            Module::new("foo::bar", Some(Visibility::Public), None),
            Module::new("foo::bar::bat", Some(Visibility::Public), None)
        ));
        assert_eq!(
            Some(&Edge::Unconnected),
            graph.edge_weight(
                Module::new("foo::bar", Some(Visibility::Public), None),
                Module::new("foo::bar::bat", Some(Visibility::Public), None)
            )
        );
    }

    #[test]
    fn module_name() {
        assert_eq!(Module::new_root("foo").name(), "foo");
        assert_eq!(
            Module::new("foo::bar", Some(Visibility::Public), None).name(),
            "bar"
        );
        assert_eq!(
            Module::new("foo::bar::baz::bat::quux", Some(Visibility::Private), None).name(),
            "quux"
        );
    }
}
<|endoftext|>


<commit_before>//! Iterative application of a differential dataflow fragment.
//!
//! The `iterate` operator takes as an argument a closure from a differential dataflow collection
//! to a collection of the same type. The output collection is the result of applying this closure
//! an unbounded number of times.
//!
//! The implementation of `iterate` does not directly apply the closure, but rather establishes an
//! iterative timely dataflow subcomputation, in which differences circulate until they dissipate
//! (indicating that the computation has reached fixed point), or until some number of iterations
//! have passed.
//!
//! **Note**: The dataflow assembled by `iterate` does not automatically insert `consolidate` for
//! you. This means that either (i) you should insert one yourself, (ii) you should be certain that
//! all paths from the input to the output of the loop involve consolidation, or (iii) you should
//! be worried that logically cancelable differences may circulate indefinitely.
//!
//! # Details
//!
//! The `iterate` method is written using a `Variable`, which lets you define your own iterative
//! computations when `iterate` itself is not sufficient. This can happen when you have two
//! collections that should evolve simultaneously, or when you would like to rotate your loop and
//! return an intermediate result.
//!
//! Using `Variable` requires more explicit arrangement of your computation, but isn't much more
//! complicated. You must define a new variable from an existing stream (its initial value), and
//! then set it to be a function of this variable (and perhaps other collections and variables).
//!
//! A `Variable` dereferences to a `Collection`, the one corresponding to its value in each iteration,
//! and it can be used in most situations where a collection can be used. The act of setting a
//! `Variable` consumes it and returns the corresponding `Collection`, preventing you from setting
//! it multiple times.

use std::fmt::Debug;
use std::ops::Deref;

use timely::progress::{Timestamp, PathSummary};
use timely::order::Product;

use timely::dataflow::*;
use timely::dataflow::scopes::child::Iterative;
use timely::dataflow::operators::{Feedback, ConnectLoop, Map};
use timely::dataflow::operators::feedback::Handle;

use ::{Data, Collection};
use ::difference::Abelian;
use lattice::Lattice;

/// An extension trait for the `iterate` method.
pub trait Iterate<G: Scope, D: Data, R: Abelian> {
    /// Iteratively apply `logic` to the source collection until convergence.
    ///
    /// Importantly, this method does not automatically consolidate results.
    /// It may be important to conclude with `consolidate()` to ensure that
    /// logically empty collections that contain cancelling records do not
    /// result in non-termination. Operators like `group`, `distinct`, and
    /// `count` also perform consolidation, and are safe to conclude with.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate timely;
    /// extern crate differential_dataflow;
    ///
    /// use differential_dataflow::input::Input;
    /// use differential_dataflow::operators::Iterate;
    /// use differential_dataflow::operators::Consolidate;
    ///
    /// fn main() {
    ///     ::timely::example(|scope| {
    ///
    ///         scope.new_collection_from(1 .. 10u32).1
    ///              .iterate(|values| {
    ///                  values.map(|x| if x % 2 == 0 { x/2 } else { x })
    ///                        .consolidate()
    ///              });
    ///     });
    /// }
    /// ```
    fn iterate<F>(&self, logic: F) -> Collection<G, D, R>
        where
            G::Timestamp: Lattice,
            for<'a> F: FnOnce(&Collection<Iterative<'a, G, u64>, D, R>)->Collection<Iterative<'a, G, u64>, D, R>;
}

impl<G: Scope, D: Ord+Data+Debug, R: Abelian> Iterate<G, D, R> for Collection<G, D, R> {
    fn iterate<F>(&self, logic: F) -> Collection<G, D, R>
        where G::Timestamp: Lattice,
              for<'a> F: FnOnce(&Collection<Iterative<'a, G, u64>, D, R>)->Collection<Iterative<'a, G, u64>, D, R> {

        self.inner.scope().scoped("Iterate", |subgraph| {
            // create a new variable, apply logic, bind variable, return.
            //
            // this could be much more succinct if we returned the collection
            // wrapped by `variable`, but it also results in substantially more
            // diffs produced; `result` is post-consolidation, and means fewer
            // records are yielded out of the loop.
            let variable = Variable::new_from(self.enter(subgraph), Product::new(Default::default(), 1));
            let result = logic(&variable);
            variable.set(&result);
            result.leave()
        })
    }
}

/// A recursively defined collection.
///
/// The `Variable` struct allows differential dataflow programs requiring more sophisticated
/// iterative patterns than singly recursive iteration. For example: in mutual recursion two
/// collections evolve simultaneously.
///
/// # Examples
///
/// The following example is equivalent to the example for the `Iterate` trait.
///
/// ```
/// extern crate timely;
/// extern crate differential_dataflow;
///
/// use timely::order::Product;
/// use timely::dataflow::Scope;
///
/// use differential_dataflow::input::Input;
/// use differential_dataflow::operators::iterate::Variable;
/// use differential_dataflow::operators::Consolidate;
///
/// fn main() {
///     ::timely::example(|scope| {
///
///         let numbers = scope.new_collection_from(1 .. 10u32).1;
///
///         scope.iterative::<u64,_,_>(|nested| {
///             let summary = Product::new(Default::default(), 1);
///             let variable = Variable::new_from(numbers.enter(nested), summary);
///             let result = variable.map(|x| if x % 2 == 0 { x/2 } else { x })
///                                  .consolidate();
///             variable.set(&result)
///                     .leave()
///         });
///     })
/// }
/// ```
pub struct Variable<G: Scope, D: Data, R: Abelian>
where G::Timestamp: Lattice {
    collection: Collection<G, D, R>,
    feedback: Handle<G, (D, G::Timestamp, R)>,
    source: Collection<G, D, R>,
    step: <G::Timestamp as Timestamp>::Summary,
}

impl<G: Scope, D: Data, R: Abelian> Variable<G, D, R> where G::Timestamp: Lattice {
    /// Creates a new initially empty `Variable`.
    pub fn new(scope: &mut G, step: <G::Timestamp as Timestamp>::Summary) -> Self {
        use collection::AsCollection;
        let empty = ::timely::dataflow::operators::generic::operator::empty(scope).as_collection();
        Self::new_from(empty, step)
    }

    /// Creates a new `Variable` from a supplied `source` stream.
    pub fn new_from(source: Collection<G, D, R>, step: <G::Timestamp as Timestamp>::Summary) -> Self {
        let (feedback, updates) = source.inner.scope().feedback(step.clone());
        let collection = Collection::new(updates).concat(&source);
        Variable { collection, feedback, source, step }
    }

    /// Adds a new source of data to the `Variable`.
    pub fn set(self, result: &Collection<G, D, R>) -> Collection<G, D, R> {
        let step = self.step;
        self.source
            .negate()
            .concat(result)
            .inner
            .flat_map(move |(x,t,d)| step.results_in(&t).map(|t| (x,t,d)))
            .connect_loop(self.feedback);

        self.collection
    }
}

impl<G: Scope, D: Data, R: Abelian> Deref for Variable<G, D, R> where G::Timestamp: Lattice {
    type Target = Collection<G, D, R>;
    fn deref(&self) -> &Self::Target {
        &self.collection
    }
}<commit_msg>monoidal iterate<commit_after>//! Iterative application of a differential dataflow fragment.
//!
//! The `iterate` operator takes as an argument a closure from a differential dataflow collection
//! to a collection of the same type. The output collection is the result of applying this closure
//! an unbounded number of times.
//!
//! The implementation of `iterate` does not directly apply the closure, but rather establishes an
//! iterative timely dataflow subcomputation, in which differences circulate until they dissipate
//! (indicating that the computation has reached fixed point), or until some number of iterations
//! have passed.
//!
//! **Note**: The dataflow assembled by `iterate` does not automatically insert `consolidate` for
//! you. This means that either (i) you should insert one yourself, (ii) you should be certain that
//! all paths from the input to the output of the loop involve consolidation, or (iii) you should
//! be worried that logically cancelable differences may circulate indefinitely.
//!
//! # Details
//!
//! The `iterate` method is written using a `Variable`, which lets you define your own iterative
//! computations when `iterate` itself is not sufficient. This can happen when you have two
//! collections that should evolve simultaneously, or when you would like to rotate your loop and
//! return an intermediate result.
//!
//! Using `Variable` requires more explicit arrangement of your computation, but isn't much more
//! complicated. You must define a new variable from an existing stream (its initial value), and
//! then set it to be a function of this variable (and perhaps other collections and variables).
//!
//! A `Variable` dereferences to a `Collection`, the one corresponding to its value in each iteration,
//! and it can be used in most situations where a collection can be used. The act of setting a
//! `Variable` consumes it and returns the corresponding `Collection`, preventing you from setting
//! it multiple times.

use std::fmt::Debug;
use std::ops::Deref;

use timely::progress::{Timestamp, PathSummary};
use timely::order::Product;

use timely::dataflow::*;
use timely::dataflow::scopes::child::Iterative;
use timely::dataflow::operators::{Feedback, ConnectLoop, Map};
use timely::dataflow::operators::feedback::Handle;

use ::{Data, Collection};
use ::difference::{Monoid, Abelian};
use lattice::Lattice;

/// An extension trait for the `iterate` method.
pub trait Iterate<G: Scope, D: Data, R: Abelian> {
    /// Iteratively apply `logic` to the source collection until convergence.
    ///
    /// Importantly, this method does not automatically consolidate results.
    /// It may be important to conclude with `consolidate()` to ensure that
    /// logically empty collections that contain cancelling records do not
    /// result in non-termination. Operators like `group`, `distinct`, and
    /// `count` also perform consolidation, and are safe to conclude with.
    ///
    /// # Examples
    ///
    /// ```
    /// extern crate timely;
    /// extern crate differential_dataflow;
    ///
    /// use differential_dataflow::input::Input;
    /// use differential_dataflow::operators::Iterate;
    /// use differential_dataflow::operators::Consolidate;
    ///
    /// fn main() {
    ///     ::timely::example(|scope| {
    ///
    ///         scope.new_collection_from(1 .. 10u32).1
    ///              .iterate(|values| {
    ///                  values.map(|x| if x % 2 == 0 { x/2 } else { x })
    ///                        .consolidate()
    ///              });
    ///     });
    /// }
    /// ```
    fn iterate<F>(&self, logic: F) -> Collection<G, D, R>
        where
            G::Timestamp: Lattice,
            for<'a> F: FnOnce(&Collection<Iterative<'a, G, u64>, D, R>)->Collection<Iterative<'a, G, u64>, D, R>;
}

impl<G: Scope, D: Ord+Data+Debug, R: Abelian> Iterate<G, D, R> for Collection<G, D, R> {
    fn iterate<F>(&self, logic: F) -> Collection<G, D, R>
        where G::Timestamp: Lattice,
              for<'a> F: FnOnce(&Collection<Iterative<'a, G, u64>, D, R>)->Collection<Iterative<'a, G, u64>, D, R> {

        self.inner.scope().scoped("Iterate", |subgraph| {
            // create a new variable, apply logic, bind variable, return.
            //
            // this could be much more succinct if we returned the collection
            // wrapped by `variable`, but it also results in substantially more
            // diffs produced; `result` is post-consolidation, and means fewer
            // records are yielded out of the loop.
            let variable = Variable::new_from(self.enter(subgraph), Product::new(Default::default(), 1));
            let result = logic(&variable);
            variable.set(&result);
            result.leave()
        })
    }
}

/// A recursively defined collection.
///
/// The `Variable` struct allows differential dataflow programs requiring more sophisticated
/// iterative patterns than singly recursive iteration. For example: in mutual recursion two
/// collections evolve simultaneously.
///
/// # Examples
///
/// The following example is equivalent to the example for the `Iterate` trait.
///
/// ```
/// extern crate timely;
/// extern crate differential_dataflow;
///
/// use timely::order::Product;
/// use timely::dataflow::Scope;
///
/// use differential_dataflow::input::Input;
/// use differential_dataflow::operators::iterate::Variable;
/// use differential_dataflow::operators::Consolidate;
///
/// fn main() {
///     ::timely::example(|scope| {
///
///         let numbers = scope.new_collection_from(1 .. 10u32).1;
///
///         scope.iterative::<u64,_,_>(|nested| {
///             let summary = Product::new(Default::default(), 1);
///             let variable = Variable::new_from(numbers.enter(nested), summary);
///             let result = variable.map(|x| if x % 2 == 0 { x/2 } else { x })
///                                  .consolidate();
///             variable.set(&result)
///                     .leave()
///         });
///     })
/// }
/// ```
pub struct Variable<G: Scope, D: Data, R: Abelian>
where G::Timestamp: Lattice {
    collection: Collection<G, D, R>,
    feedback: Handle<G, (D, G::Timestamp, R)>,
    source: Collection<G, D, R>,
    step: <G::Timestamp as Timestamp>::Summary,
}

impl<G: Scope, D: Data, R: Abelian> Variable<G, D, R> where G::Timestamp: Lattice {
    /// Creates a new initially empty `Variable`.
    pub fn new(scope: &mut G, step: <G::Timestamp as Timestamp>::Summary) -> Self {
        use collection::AsCollection;
        let empty = ::timely::dataflow::operators::generic::operator::empty(scope).as_collection();
        Self::new_from(empty, step)
    }

    /// Creates a new `Variable` from a supplied `source` stream.
    pub fn new_from(source: Collection<G, D, R>, step: <G::Timestamp as Timestamp>::Summary) -> Self {
        let (feedback, updates) = source.inner.scope().feedback(step.clone());
        let collection = Collection::new(updates).concat(&source);
        Variable { collection, feedback, source, step }
    }

    /// Adds a new source of data to the `Variable`.
    pub fn set(self, result: &Collection<G, D, R>) -> Collection<G, D, R> {
        let step = self.step;
        self.source
            .negate()
            .concat(result)
            .inner
            .flat_map(move |(x,t,d)| step.results_in(&t).map(|t| (x,t,d)))
            .connect_loop(self.feedback);

        self.collection
    }
}

impl<G: Scope, D: Data, R: Abelian> Deref for Variable<G, D, R> where G::Timestamp: Lattice {
    type Target = Collection<G, D, R>;
    fn deref(&self) -> &Self::Target {
        &self.collection
    }
}

/// A recursively defined collection.
///
/// The `Variable` struct allows differential dataflow programs requiring more sophisticated
/// iterative patterns than singly recursive iteration. For example: in mutual recursion two
/// collections evolve simultaneously.
///
/// # Examples
///
/// The following example is equivalent to the example for the `Iterate` trait.
///
/// ```
/// extern crate timely;
/// extern crate differential_dataflow;
///
/// use timely::order::Product;
/// use timely::dataflow::Scope;
///
/// use differential_dataflow::input::Input;
/// use differential_dataflow::operators::iterate::Variable;
/// use differential_dataflow::operators::Consolidate;
///
/// fn main() {
///     ::timely::example(|scope| {
///
///         let numbers = scope.new_collection_from(1 .. 10u32).1;
///
///         scope.iterative::<u64,_,_>(|nested| {
///             let summary = Product::new(Default::default(), 1);
///             let variable = MonoidVariable::new(summary);
///             let result = variable.map(|x| if x % 2 == 0 { x/2 } else { x })
///                                  .consolidate();
///             variable.set(&result)
///                     .leave()
///         });
///     })
/// }
/// ```
pub struct MonoidVariable<G: Scope, D: Data, R: Monoid>
where G::Timestamp: Lattice {
    collection: Collection<G, D, R>,
    feedback: Handle<G, (D, G::Timestamp, R)>,
    step: <G::Timestamp as Timestamp>::Summary,
}

impl<G: Scope, D: Data, R: Monoid> MonoidVariable<G, D, R> where G::Timestamp: Lattice {
    /// Creates a new initially empty `Variable`.
    pub fn new(scope: &mut G, step: <G::Timestamp as Timestamp>::Summary) -> Self {
        let (feedback, updates) = scope.feedback(step.clone());
        let collection = Collection::new(updates);
        MonoidVariable { collection, feedback, step }
    }

    /// Adds a new source of data to the `Variable`.
    pub fn set(self, result: &Collection<G, D, R>) -> Collection<G, D, R> {
        let step = self.step;
        result
            .inner
            .flat_map(move |(x,t,d)| step.results_in(&t).map(|t| (x,t,d)))
            .connect_loop(self.feedback);

        self.collection
    }
}

impl<G: Scope, D: Data, R: Monoid> Deref for MonoidVariable<G, D, R> where G::Timestamp: Lattice {
    type Target = Collection<G, D, R>;
    fn deref(&self) -> &Self::Target {
        &self.collection
    }
}<|endoftext|>


<commit_before>use collectors;
use events;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::mem;

pub enum Item {
    Done,
    Emit(events::Event)
}

pub struct PipelineRef {
    chan: Sender<Item>
}

static mut PIPELINE: *const PipelineRef = 0 as *const PipelineRef;

pub struct Pipeline {
    chan: Sender<Item>,
    worker: Option<thread::JoinHandle<()>>
}

impl Drop for Pipeline {
    fn drop(&mut self) {
        self.chan.send(Item::Done).is_ok();
        let thread = mem::replace(&mut self.worker, None);
        if let Some(handle) = thread {
            handle.join().is_ok();
        }
    }
}

pub fn emit(event: events::Event) {
    // Should be an atomic read, etc.
    let p = unsafe { PIPELINE };
    if p != 0 as *const PipelineRef {
        // Not sound, as chan is not Sync :-(
        unsafe {
            (*p).chan.send(Item::Emit(event)).is_ok();
        }
    }
}

pub fn init<T: collectors::Collector + Send + 'static>(collector: T) -> Pipeline {
    let (tx, rx) = channel::<Item>();
    unsafe {
        let pr = Box::new(PipelineRef { chan: tx.clone() });
        // Should be atomic CAS etc.
        PIPELINE = mem::transmute::<Box<PipelineRef>, *const PipelineRef>(pr);
    }

    let coll = collector;
    let child = thread::spawn(move|| {
        loop {
            let done = match rx.recv().unwrap() {
                Item::Done => true,
                Item::Emit(payload) => {
                    if let Err(e) = coll.dispatch(&vec![payload]) {
                        error!("Could not dispatch events: {}", e);
                    }
                    false
                }
            };

            if done {
                break;
            }
        }
    });

    Pipeline {
         worker: Some(child),
         chan: tx
    }
}
<commit_msg>Synchronize access to the sending side of the pipeline<commit_after>use collectors;
use events;
use std::sync::mpsc::{channel, Sender};
use std::thread;
use std::mem;
use std::sync;

pub enum Item {
    Done,
    Emit(events::Event)
}

pub struct PipelineRef {
    chan: sync::Mutex<Sender<Item>>
}

static mut PIPELINE: *const PipelineRef = 0 as *const PipelineRef;

pub struct Pipeline {
    chan: Sender<Item>,
    worker: Option<thread::JoinHandle<()>>
}

impl Drop for Pipeline {
    fn drop(&mut self) {
        self.chan.send(Item::Done).is_ok();
        let thread = mem::replace(&mut self.worker, None);
        if let Some(handle) = thread {
            handle.join().is_ok();
        }
    }
}

pub fn emit(event: events::Event) {
    // Should be an atomic read, etc.
    let p = unsafe { PIPELINE };
    if p != 0 as *const PipelineRef {
        unsafe {
            (*p).chan.lock().unwrap().send(Item::Emit(event)).is_ok();
        }
    }
}

pub fn init<T: collectors::Collector + Send + 'static>(collector: T) -> Pipeline {
    let (tx, rx) = channel::<Item>();
    unsafe {
        let pr = Box::new(PipelineRef { chan: sync::Mutex::new(tx.clone()) });
        // Should be atomic CAS etc.
        PIPELINE = mem::transmute::<Box<PipelineRef>, *const PipelineRef>(pr);
    }

    let coll = collector;
    let child = thread::spawn(move|| {
        loop {
            let done = match rx.recv().unwrap() {
                Item::Done => true,
                Item::Emit(payload) => {
                    if let Err(e) = coll.dispatch(&vec![payload]) {
                        error!("Could not dispatch events: {}", e);
                    }
                    false
                }
            };

            if done {
                break;
            }
        }
    });

    Pipeline {
         worker: Some(child),
         chan: tx
    }
}
<|endoftext|>


<commit_before>use core::cmp::{Eq, Ord};
use std::list;
use std::list::{List, Cons, Nil};

// I would really love for these to return Heaps but my knowledge
// of Rust is too limited to make that work.
pub trait Heap<E: Copy Eq Ord> {
  pure fn is_empty() -> bool;
  pure fn insert(elem: E) -> PairingHeap<E>;
  pure fn find_min() -> (Option<E>, PairingHeap<E>);
  pure fn delete_min() -> (Option<E>, PairingHeap<E>);
}

/**
 * A purely functional Pairing Heap [FSST86]
 *
 * Our implementation uses Linked List (cons cells) so may not be the
 * fastest way to implement this in Rust.
 *
 * This implementation is a port of the Standard ML found in Okasaki's
 * Purely Functional Data Structures.
 */
pub enum PairingHeap<E: Copy Eq Ord> {
  Empty_,
  PairingHeap_(@{
    head: E,
    rest: @List<PairingHeap<E>>
  })
}

pure fn Empty<E: Copy Eq Ord>() -> PairingHeap<E> {
  return Empty_;
}

pure fn PairingHeap<E: Copy Eq Ord>(initial_value: E) -> PairingHeap<E> {
  return PairingHeap_(@{
    head: initial_value,
    rest: @Nil
  });
}

impl<E: Copy Eq Ord> PairingHeap<E> : Eq {
  pure fn eq(other: &PairingHeap<E>) -> bool {
    match (self, *other) {
      (Empty_, Empty_) => {true}
      (PairingHeap_(heapA), PairingHeap_(heapB)) => {
        return (heapA.head == heapB.head) && (heapA.rest == heapB.rest);
      }
      //(Empty_, PairingHeap_(_)) => {false} // why are these unreachable?
      //(PairingHeap_(_), Empty) => {false}
      (_, _) => {false}
    }
  }

  pure fn ne(other: &PairingHeap<E>) -> bool { !(self).eq(other) }
}

impl<E: Copy Eq Ord> PairingHeap<E> : Heap<E> {
  pure fn is_empty() -> bool {
    match self {
      Empty_ => { return true; }
      _ => {return false; }
    };
  }

  pure fn insert(e: E) -> PairingHeap<E> {
    return self.merge(PairingHeap(e));
  }

  pure fn find_min() -> (Option<E>, PairingHeap<E>) {
    match self {
      Empty_ => { return (None, self); }
      p@PairingHeap_(heap) => { return (Some(heap.head), p); }
    }
  }

  // Deletes the minimum item without returning it
  pure fn delete_min() -> (Option<E>, PairingHeap<E>) {
    match self {
      Empty_ => {(None, self)}
      PairingHeap_(heap) => {(Some(heap.head), self.mergePairs(heap.rest))}
    }
  }

  pure fn merge(other: PairingHeap<E>) -> PairingHeap<E> {
    match (self, other) {
      (Empty_, b) => { return b; }
      (a, Empty_) => { return a; }
      (x@PairingHeap_(heapA), y@PairingHeap_(heapB)) => {
        if (heapA.head.le(&heapB.head)) {
          return PairingHeap_(@{
            head: heapA.head,
            rest: @Cons(y, heapA.rest)
          });
        } else {
          return PairingHeap_(@{
            head: heapB.head,
            rest: @Cons(x, heapB.rest)
          });
        }
      }
    }
  }

  pure fn mergePairs(heaps: @List<PairingHeap<E>>) -> PairingHeap<E> {
    match heaps {
      // Why are @-signs required for pattern matching here?
      @Cons(a, @Cons(b, xs)) => {a.merge(b).merge(self.mergePairs(xs))}
      @Cons(elem, @Nil) => {elem}
      @Nil => {Empty()}
    }
  }
}

#[test]
fn test_heap_create() {
  let heap = PairingHeap(1);
  assert(!heap.is_empty());

  // inference fails on this without a type declaration.
  let heap : PairingHeap<()> = Empty();
  assert(heap.is_empty());
}

#[test]
fn test_heap_insert() {
  let v1 = PairingHeap(10);
  let v2 = v1.insert(1);
  let (one, v3) = v2.delete_min();
  let (ten, v4) = v3.delete_min();
  let (e, v5) = v4.delete_min();

  assert(one == Some(1));
  assert(ten == Some(10));
  assert(v4 == Empty_);
  assert(e == None);
  assert(v5 == Empty_);
}

#[test]
fn test_heap_insert_delete_interleved() {
  let v1 = PairingHeap(10);
  let (a, v2) = v1.delete_min();
  assert(a == Some(10));
  assert(v2 == Empty_);

  let v3 = v2.insert(9);
  let v4 = v3.insert(8);
  let v5 = v4.insert(11);

  let (b, v6) = v5.delete_min();
  assert(b == Some(8));

  let v7 = v6.insert(7);
  let v8 = v7.insert(12);

  let (c, v9) = v8.delete_min();
  assert(c == Some(7));

  let (d, v10) = v9.delete_min();
  assert(d == Some(9));

  let (e, v11) = v10.delete_min();
  assert(e == Some(11));

  let (f, v12) = v11.delete_min();
  assert(f == Some(12));

  let (g, v13) = v12.delete_min();
  assert(g == None);
  assert(v13 == Empty_);
}

#[test]
fn test_immutable_heap() {
  let heap = PairingHeap(10);
  let x1 = heap.insert(1);

  let (a, v1) = heap.delete_min();
  assert(a == Some(10));
  assert(v1 == Empty_);

  let (b, x2) = x1.delete_min();
  assert(b == Some(1));
  assert(x2 != Empty_);
}

<commit_msg>moving the enum and docs to the top<commit_after>use core::cmp::{Eq, Ord};
use std::list;
use std::list::{List, Cons, Nil};

/**
 * A purely functional Pairing Heap [FSST86]
 *
 * Our implementation uses Linked List (cons cells) so may not be the
 * fastest way to implement this in Rust.
 *
 * This implementation is a port of the Standard ML found in Okasaki's
 * Purely Functional Data Structures.
 */
pub enum PairingHeap<E: Copy Eq Ord> {
  Empty_,
  PairingHeap_(@{
    head: E,
    rest: @List<PairingHeap<E>>
  })
}

pub trait Heap<E: Copy Eq Ord> {
  // I would really love for these to return Heaps but my knowledge
  // of Rust is too limited to make that work.
  pure fn is_empty() -> bool;
  pure fn insert(elem: E) -> PairingHeap<E>;
  pure fn find_min() -> (Option<E>, PairingHeap<E>);
  pure fn delete_min() -> (Option<E>, PairingHeap<E>);
}

pure fn Empty<E: Copy Eq Ord>() -> PairingHeap<E> {
  return Empty_;
}

pure fn PairingHeap<E: Copy Eq Ord>(initial_value: E) -> PairingHeap<E> {
  return PairingHeap_(@{
    head: initial_value,
    rest: @Nil
  });
}

impl<E: Copy Eq Ord> PairingHeap<E> : Eq {
  pure fn eq(other: &PairingHeap<E>) -> bool {
    match (self, *other) {
      (Empty_, Empty_) => {true}
      (PairingHeap_(heapA), PairingHeap_(heapB)) => {
        return (heapA.head == heapB.head) && (heapA.rest == heapB.rest);
      }
      //(Empty_, PairingHeap_(_)) => {false} // why are these unreachable?
      //(PairingHeap_(_), Empty) => {false}
      (_, _) => {false}
    }
  }

  pure fn ne(other: &PairingHeap<E>) -> bool { !(self).eq(other) }
}

impl<E: Copy Eq Ord> PairingHeap<E> : Heap<E> {
  pure fn is_empty() -> bool {
    match self {
      Empty_ => { return true; }
      _ => {return false; }
    };
  }

  pure fn insert(e: E) -> PairingHeap<E> {
    return self.merge(PairingHeap(e));
  }

  pure fn find_min() -> (Option<E>, PairingHeap<E>) {
    match self {
      Empty_ => { return (None, self); }
      p@PairingHeap_(heap) => { return (Some(heap.head), p); }
    }
  }

  // Deletes the minimum item without returning it
  pure fn delete_min() -> (Option<E>, PairingHeap<E>) {
    match self {
      Empty_ => {(None, self)}
      PairingHeap_(heap) => {(Some(heap.head), self.mergePairs(heap.rest))}
    }
  }

  pure fn merge(other: PairingHeap<E>) -> PairingHeap<E> {
    match (self, other) {
      (Empty_, b) => { return b; }
      (a, Empty_) => { return a; }
      (x@PairingHeap_(heapA), y@PairingHeap_(heapB)) => {
        if (heapA.head.le(&heapB.head)) {
          return PairingHeap_(@{
            head: heapA.head,
            rest: @Cons(y, heapA.rest)
          });
        } else {
          return PairingHeap_(@{
            head: heapB.head,
            rest: @Cons(x, heapB.rest)
          });
        }
      }
    }
  }

  pure fn mergePairs(heaps: @List<PairingHeap<E>>) -> PairingHeap<E> {
    match heaps {
      // Why are @-signs required for pattern matching here?
      @Cons(a, @Cons(b, xs)) => {a.merge(b).merge(self.mergePairs(xs))}
      @Cons(elem, @Nil) => {elem}
      @Nil => {Empty()}
    }
  }
}

#[test]
fn test_heap_create() {
  let heap = PairingHeap(1);
  assert(!heap.is_empty());

  // inference fails on this without a type declaration.
  let heap : PairingHeap<()> = Empty();
  assert(heap.is_empty());
}

#[test]
fn test_heap_insert() {
  let v1 = PairingHeap(10);
  let v2 = v1.insert(1);
  let (one, v3) = v2.delete_min();
  let (ten, v4) = v3.delete_min();
  let (e, v5) = v4.delete_min();

  assert(one == Some(1));
  assert(ten == Some(10));
  assert(v4 == Empty_);
  assert(e == None);
  assert(v5 == Empty_);
}

#[test]
fn test_heap_insert_delete_interleved() {
  let v1 = PairingHeap(10);
  let (a, v2) = v1.delete_min();
  assert(a == Some(10));
  assert(v2 == Empty_);

  let v3 = v2.insert(9);
  let v4 = v3.insert(8);
  let v5 = v4.insert(11);

  let (b, v6) = v5.delete_min();
  assert(b == Some(8));

  let v7 = v6.insert(7);
  let v8 = v7.insert(12);

  let (c, v9) = v8.delete_min();
  assert(c == Some(7));

  let (d, v10) = v9.delete_min();
  assert(d == Some(9));

  let (e, v11) = v10.delete_min();
  assert(e == Some(11));

  let (f, v12) = v11.delete_min();
  assert(f == Some(12));

  let (g, v13) = v12.delete_min();
  assert(g == None);
  assert(v13 == Empty_);
}

#[test]
fn test_immutable_heap() {
  let heap = PairingHeap(10);
  let x1 = heap.insert(1);

  let (a, v1) = heap.delete_min();
  assert(a == Some(10));
  assert(v1 == Empty_);

  let (b, x2) = x1.delete_min();
  assert(b == Some(1));
  assert(x2 != Empty_);
}

<|endoftext|>