<commit_before>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{self, AppVec, ArcType, BuiltinType, Generic, Type},
};

use crate::{
    substitution::{is_variable_unified, Substitution},
    typecheck::{TypeError, Typecheck},
};

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<Symbol, Generic<Symbol>>,
    variable_generator: TypeVariableGenerator,
    tc: &'a mut Typecheck<'b>,
    context: ArcType,
    span: Span<BytePos>,
    in_generic_replace: bool,
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &ArcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: FnvMap::default(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            context: typ.clone(),
            span,
            in_generic_replace: false,
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.tc.type_variables.enter_scope();
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
                self.generalizer.tc.type_variables.exit_scope();
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                {
                    let type_cache = &self.generalizer.tc.type_cache;
                    self.generalizer.tc.type_variables.extend(
                        typ.forall_params()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }
                if let Some(finished) = self.generalizer.generalize_type(typ) {
                    *typ = finished;
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut ArcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(_, generic)| generic)
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            Type::forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &ArcType) -> Option<ArcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        trace!("GEN {}", typ);
        match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                // Create a prefix if none exists
                let id = self.variable_generator.next_variable(self.tc);

                let gen: ArcType = Type::generic(Generic::new(id.clone(), var.kind.clone()));
                debug!("Gen {} to {}", var.id, gen);
                self.subs.insert(var.id, gen.clone());

                self.unbound_variables
                    .insert(id.clone(), Generic::new(id, var.kind.clone()));

                Some(gen)
            }

            Type::Forall(ref params, ref typ, Some(ref vars)) => {
                trace!("Generalize `{}` {:?}", typ, vars);

                self.type_variables.enter_scope();
                self.type_variables.extend(
                    params
                        .iter()
                        .zip(vars)
                        .map(|(param, var)| (param.id.clone(), var.clone())),
                );

                trace!("Generalize forall => `{}`", typ);

                let new_type = self.generalize_type(&typ);
                self.type_variables.exit_scope();

                Some(Type::forall(
                    params
                        .iter()
                        .zip(vars)
                        .filter(|&(_, var)| !is_variable_unified(&self.subs, var))
                        .map(|(param, _)| param.clone())
                        .collect(),
                    new_type.unwrap_or_else(|| typ.clone()),
                ))
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if !in_scope {
                    self.tc.error(
                        self.span,
                        TypeError::Message(format!(
                            "Skolem variable `{}` would escape as it is not bound in `{}`",
                            skolem.name, self.context
                        )),
                    );
                }
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    };

                    if !in_scope {
                        self.unbound_variables
                            .insert(generic.id.clone(), generic.clone());
                    }

                    Some(Type::generic(generic))
                } else {
                    replacement
                }
            }

            Type::Generic(ref generic) => match self.type_variables.get(&generic.id).cloned() {
                Some(var) => {
                    trace!("GEN2 {} => {}", generic.id, var);
                    if !self.in_generic_replace {
                        self.in_generic_replace = true;
                        let new = self.generalize_type(&var).or(replacement);
                        self.in_generic_replace = false;
                        new
                    } else {
                        replacement
                    }
                }
                None => {
                    self.unbound_variables
                        .insert(generic.id.clone(), generic.clone());
                    replacement
                }
            },

            _ => {
                if let Type::Forall(ref params, _, None) = **typ {
                    let type_cache = &self.tc.type_cache;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                types::walk_move_type_opt(
                    typ,
                    &mut types::ControlVisitation(|typ: &ArcType| self.generalize_type(typ)),
                )
                .map(|t| unroll_typ(&t).unwrap_or(t))
                .or_else(|| replacement.clone())
            }
        }
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<ArcType>, typ: &ArcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<ArcType>, typ: &ArcType) {
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_move_type_opt(
                typ,
                &mut types::ControlVisitation(|typ: &ArcType| {
                    gather_foralls(map, subs, typ);
                    None
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        tc.type_variables.insert(symbol.clone(), Type::hole());
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(typ: &ArcType) -> Option<ArcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(Type::function(args.into_iter().collect(), ret))
            }
            _ => Some(Type::app(current.clone(), args)),
        }
    }
}

fn unroll_record(typ: &Type<Symbol>) -> Option<ArcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(Type::extend_row(new_types, new_fields, current.clone()))
    }
}

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

    #[test]
    fn unroll_typ_test() {
        let i: ArcType = Type::int();
        let s: ArcType = Type::string();
        assert_eq!(
            unroll_typ(&Type::app(
                Type::app(i.clone(), collect![s.clone()]),
                collect![i.clone()]
            )),
            Some(Type::app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(&Type::app(
                Type::app(i.clone(), collect![i.clone()]),
                collect![s.clone()]
            )),
            Some(Type::app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: ArcType = Type::builtin(BuiltinType::Function);
        assert_eq!(
            unroll_typ(&Type::app(
                Type::app(f.clone(), collect![i.clone()]),
                collect![s.clone()]
            )),
            Some(Type::function(collect![i.clone()], s.clone()))
        );
    }
}
<commit_msg>Revert some typecheck changes which did not work out exactly<commit_after>use crate::base::{
    ast::{self, MutVisitor, SpannedExpr, SpannedIdent},
    fnv::{FnvMap, FnvSet},
    merge,
    pos::{BytePos, Span},
    symbol::Symbol,
    types::{self, AppVec, ArcType, BuiltinType, Field, Generic, Type},
};

use crate::{
    substitution::{is_variable_unified, Substitution},
    typecheck::{TypeError, Typecheck},
};

pub(crate) struct TypeGeneralizer<'a, 'b: 'a> {
    level: u32,
    unbound_variables: FnvMap<Symbol, Generic<Symbol>>,
    variable_generator: TypeVariableGenerator,
    tc: &'a mut Typecheck<'b>,
    context: ArcType,
    span: Span<BytePos>,
}

impl<'a, 'b> ::std::ops::Deref for TypeGeneralizer<'a, 'b> {
    type Target = Typecheck<'b>;
    fn deref(&self) -> &Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> ::std::ops::DerefMut for TypeGeneralizer<'a, 'b> {
    fn deref_mut(&mut self) -> &mut Typecheck<'b> {
        self.tc
    }
}

impl<'a, 'b> TypeGeneralizer<'a, 'b> {
    pub(crate) fn new(
        level: u32,
        tc: &'a mut Typecheck<'b>,
        typ: &ArcType,
        span: Span<BytePos>,
    ) -> TypeGeneralizer<'a, 'b> {
        TypeGeneralizer {
            level,
            unbound_variables: FnvMap::default(),
            variable_generator: TypeVariableGenerator::new(&tc.subs, typ),
            tc,
            context: typ.clone(),
            span,
        }
    }

    /// Generalizing updates all variables which are above `level` into "generic variables". A
    /// generic variable is instantiated with a fresh type variable each time it is refered to.
    ///
    /// Generalzing is a crucial part when inferring types as types will otherwise be less general
    /// than they need to. Consider the following expression.
    /// ```f#
    /// let id x = x
    /// in id 2
    /// ```
    /// If the variable in `id` was not generalized before the `id 2` call the variable would be
    /// unified to an `Int` which is not what we want. Generalazing before checking the body of the
    /// `let` basically infers that the variables in `id` does not refer to anything outside the
    /// `let` scope and can thus be "generalized" into `a -> a` which is instantiated with a fresh
    /// type variable in the `id 2` call.
    pub(crate) fn generalize_variables<'i>(
        &mut self,
        args: &mut Iterator<Item = &'i mut SpannedIdent<Symbol>>,
        expr: &mut SpannedExpr<Symbol>,
    ) {
        self.tc.type_variables.enter_scope();

        // Replaces all type variables with their inferred types
        struct ReplaceVisitor<'a: 'c, 'b: 'a, 'c> {
            generalizer: &'c mut TypeGeneralizer<'a, 'b>,
        }

        impl<'a, 'b, 'c, 'd> MutVisitor<'d> for ReplaceVisitor<'a, 'b, 'c> {
            type Ident = Symbol;

            fn visit_expr(&mut self, e: &'d mut SpannedExpr<Self::Ident>) {
                self.generalizer.tc.type_variables.enter_scope();
                self.generalizer.span = e.span;
                ast::walk_mut_expr(self, e);
                self.generalizer.tc.type_variables.exit_scope();
            }

            fn visit_spanned_typed_ident(&mut self, id: &mut SpannedIdent<Symbol>) {
                self.generalizer.span = id.span;
                self.visit_ident(&mut id.value)
            }
            fn visit_typ(&mut self, typ: &mut ArcType) {
                {
                    let type_cache = &self.generalizer.tc.type_cache;
                    self.generalizer.tc.type_variables.extend(
                        typ.forall_params()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }
                if let Some(finished) = self.generalizer.generalize_type(typ) {
                    *typ = finished;
                }
            }
        }

        {
            let mut visitor = ReplaceVisitor { generalizer: self };
            visitor.visit_expr(expr);
            for arg in &mut *args {
                visitor.visit_typ(&mut arg.value.typ)
            }
        }

        self.tc.type_variables.exit_scope();
    }

    pub(crate) fn generalize_type_top(&mut self, typ: &mut ArcType) {
        self.tc.type_variables.enter_scope();

        let mut result_type = self.generalize_type(typ);

        self.tc.type_variables.exit_scope();

        if result_type.is_none() && !self.unbound_variables.is_empty() {
            result_type = Some(typ.clone());
        }

        result_type = result_type.map(|typ| {
            let mut params = self
                .unbound_variables
                .drain()
                .map(|(_, generic)| generic)
                .collect::<Vec<_>>();
            params.sort_unstable_by(|l, r| l.id.declared_name().cmp(r.id.declared_name()));

            Type::forall(params, typ)
        });
        if let Some(finished) = result_type {
            *typ = finished;
        }
        debug!("End generalize {}", typ);
    }

    pub(crate) fn generalize_type(&mut self, typ: &ArcType) -> Option<ArcType> {
        let replacement = self.subs.replace_variable(typ);
        let mut typ = typ;
        if let Some(ref t) = replacement {
            typ = t;
        }
        match **typ {
            Type::Variable(ref var) if self.subs.get_level(var.id) >= self.level => {
                // Create a prefix if none exists
                let id = self.variable_generator.next_variable(self.tc);

                let gen: ArcType = Type::generic(Generic::new(id.clone(), var.kind.clone()));
                debug!("Gen {} to {}", var.id, gen);
                self.subs.insert(var.id, gen.clone());

                self.unbound_variables
                    .insert(id.clone(), Generic::new(id, var.kind.clone()));

                Some(gen)
            }

            Type::Forall(ref params, ref typ, Some(ref vars)) => {
                trace!("Generalize `{}` {:?}", typ, vars);

                let mut new_params = Vec::new();
                let typ = {
                    let subs = &self.tc.subs;
                    self.tc.named_variables.clear();
                    for (param, var) in params.iter().zip(vars) {
                        if is_variable_unified(subs, var) {
                            self.tc
                                .named_variables
                                .insert(param.id.clone(), var.clone());
                        } else {
                            new_params.push(param.clone());
                        }
                    }

                    if self.tc.named_variables.is_empty() {
                        typ.clone()
                    } else {
                        typ.instantiate_generics_(&mut self.tc.named_variables)
                            .unwrap_or(typ.clone())
                    }
                };

                self.type_variables.enter_scope();
                self.type_variables.extend(
                    params
                        .iter()
                        .zip(vars)
                        .map(|(param, var)| (param.id.clone(), var.clone())),
                );

                trace!("Generalize forall => `{}`", typ);

                let new_type = self.generalize_type(&typ);
                self.type_variables.exit_scope();

                Some(Type::forall(
                    new_params,
                    new_type.unwrap_or_else(|| typ.clone()),
                ))
            }

            Type::Skolem(ref skolem) => {
                let in_scope = self.type_variables.contains_key(&skolem.name);
                if !in_scope {
                    self.tc.error(
                        self.span,
                        TypeError::Message(format!(
                            "Skolem variable `{}` would escape as it is not bound in `{}`",
                            skolem.name, self.context
                        )),
                    );
                }
                if self.subs.get_level(skolem.id) >= self.level {
                    let generic = Generic {
                        id: skolem.name.clone(),
                        kind: skolem.kind.clone(),
                    };

                    if !in_scope {
                        self.unbound_variables
                            .insert(generic.id.clone(), generic.clone());
                    }

                    Some(Type::generic(generic))
                } else {
                    replacement
                }
            }

            _ => {
                if let Type::Forall(ref params, _, None) = **typ {
                    let type_cache = &self.tc.type_cache;
                    self.tc.type_variables.extend(
                        params
                            .iter()
                            .map(|param| (param.id.clone(), type_cache.hole())),
                    );
                }

                let new_type = types::walk_move_type_opt(
                    typ,
                    &mut types::ControlVisitation(|typ: &ArcType| self.generalize_type(typ)),
                );
                match **typ {
                    Type::Generic(ref generic)
                        if self.type_variables.get(&generic.id).is_none() =>
                    {
                        self.unbound_variables
                            .insert(generic.id.clone(), generic.clone());
                    }
                    _ => (),
                }
                new_type
                    .map(|t| unroll_typ(&t).unwrap_or(t))
                    .or_else(|| replacement.clone())
            }
        }
    }
}

struct TypeVariableGenerator {
    map: FnvSet<Symbol>,
    name: String,
    i: u32,
}

impl TypeVariableGenerator {
    fn new(subs: &Substitution<ArcType>, typ: &ArcType) -> TypeVariableGenerator {
        fn gather_foralls(map: &mut FnvSet<Symbol>, subs: &Substitution<ArcType>, typ: &ArcType) {
            let typ = subs.real(typ);
            if let Type::Forall(ref params, _, _) = **typ {
                map.extend(params.iter().map(|param| param.id.clone()));
            }
            types::walk_move_type_opt(
                typ,
                &mut types::ControlVisitation(|typ: &ArcType| {
                    gather_foralls(map, subs, typ);
                    None
                }),
            );
        }
        let mut map = FnvSet::default();
        gather_foralls(&mut map, subs, typ);
        TypeVariableGenerator {
            map,
            name: "".to_string(),
            i: 0,
        }
    }
    /// Generate a generic variable name which is not used in the current scope
    fn next_variable(&mut self, tc: &mut Typecheck) -> Symbol {
        let symbol = if self.name.is_empty() {
            self.next_variable_(tc)
        } else {
            let name = format!("{}{}", self.name, self.i);
            self.i += 1;
            tc.symbols.symbol(&name[..])
        };
        self.map.insert(symbol.clone());
        tc.type_variables.insert(symbol.clone(), Type::hole());
        symbol
    }

    fn next_variable_(&mut self, tc: &mut Typecheck) -> Symbol {
        for c in b'a'..(b'z' + 1) {
            self.name.push(c as char);
            let symbol = tc.symbols.symbol(&self.name[..]);
            if !self.map.contains(&symbol) && tc.type_variables.get(&symbol).is_none() {
                return symbol;
            }
            self.name.pop();
        }
        self.name.push('a');
        self.next_variable_(tc)
    }
}

fn unroll_typ(typ: &ArcType) -> Option<ArcType> {
    let mut args = AppVec::new();
    let mut current = match **typ {
        Type::App(ref l, ref rest) => {
            // No need to unroll if `l` is not an application as that will just result in returning
            // an application that is identical to `typ`
            match **l {
                Type::App(..) => (),
                _ => return None,
            }
            args.extend(rest.iter().rev().cloned());
            l
        }
        _ => return unroll_record(typ),
    };
    while let Type::App(ref l, ref rest) = **current {
        args.extend(rest.iter().rev().cloned());
        current = l;
    }
    if args.is_empty() {
        None
    } else {
        args.reverse();
        match **current {
            Type::Builtin(BuiltinType::Function) if args.len() == 2 => {
                let ret = args.pop().unwrap();
                Some(Type::function(args.into_iter().collect(), ret))
            }
            _ => Some(Type::app(current.clone(), args)),
        }
    }
}

fn unroll_record(typ: &Type<Symbol>) -> Option<ArcType> {
    let mut new_types = Vec::new();
    let mut new_fields = Vec::new();
    let mut current = match *typ {
        Type::ExtendRow {
            ref types,
            ref fields,
            ref rest,
        } => match **rest {
            Type::ExtendRow { .. } => {
                new_types.extend_from_slice(types);
                new_fields.extend_from_slice(fields);
                rest
            }
            _ => return None,
        },
        _ => return None,
    };
    while let Type::ExtendRow {
        ref types,
        ref fields,
        ref rest,
    } = **current
    {
        new_types.extend_from_slice(types);
        new_fields.extend_from_slice(fields);
        current = rest;
    }
    if new_types.is_empty() && new_fields.is_empty() {
        None
    } else {
        Some(Type::extend_row(new_types, new_fields, current.clone()))
    }
}

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

    #[test]
    fn unroll_typ_test() {
        let i: ArcType = Type::int();
        let s: ArcType = Type::string();
        assert_eq!(
            unroll_typ(&Type::app(
                Type::app(i.clone(), collect![s.clone()]),
                collect![i.clone()]
            )),
            Some(Type::app(i.clone(), collect![s.clone(), i.clone()]))
        );
        assert_eq!(
            unroll_typ(&Type::app(
                Type::app(i.clone(), collect![i.clone()]),
                collect![s.clone()]
            )),
            Some(Type::app(i.clone(), collect![i.clone(), s.clone()]))
        );
        let f: ArcType = Type::builtin(BuiltinType::Function);
        assert_eq!(
            unroll_typ(&Type::app(
                Type::app(f.clone(), collect![i.clone()]),
                collect![s.clone()]
            )),
            Some(Type::function(collect![i.clone()], s.clone()))
        );
    }
}
<|endoftext|>


<commit_before>//! A resource manager to load meshes.

use std::io::IoResult;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use na::{Pnt3, Vec3};
use ncollide::procedural::TriMesh3;
use ncollide::procedural;
use resource::Mesh;
use loader::obj;
use loader::mtl::MtlMaterial;

local_data_key!(KEY_MESH_MANAGER: RefCell<MeshManager>)

/// The mesh manager.
///
/// Upon construction, it contains:
///
/// It keeps a cache of already-loaded meshes. Note that this is only a cache, nothing more.
/// Thus, its usage is not required to load meshes.
pub struct MeshManager {
    meshes:       HashMap<String, Rc<RefCell<Mesh>>>
}

impl MeshManager {
    /// Creates a new mesh manager.
    pub fn new() -> MeshManager {
        let mut res = MeshManager {
            meshes: HashMap::new()
        };

        let _ = res.add_trimesh(procedural::unit_sphere(50, 50, true), false, "sphere");
        let _ = res.add_trimesh(procedural::unit_cuboid(), false, "cube");
        let _ = res.add_trimesh(procedural::unit_cone(50), false, "cone");
        let _ = res.add_trimesh(procedural::unit_cylinder(50), false, "cylinder");

        res
    }

    /// Mutably applies a function to the mesh manager.
    pub fn get_global_manager<T>(f: |&mut MeshManager| -> T) -> T {
        if KEY_MESH_MANAGER.get().is_none() {
            let _ = KEY_MESH_MANAGER.replace(Some(RefCell::new(MeshManager::new())));
        }

        f(KEY_MESH_MANAGER.get().unwrap().borrow_mut().deref_mut())
    }

    /// Get a mesh with the specified name. Returns `None` if the mesh is not registered.
    pub fn get(&mut self, name: &str) -> Option<Rc<RefCell<Mesh>>> {
        self.meshes.get(&name.to_string()).map(|t| t.clone())
    }

    /// Adds a mesh with the specified name to this cache.
    pub fn add(&mut self, mesh: Rc<RefCell<Mesh>>, name: &str) {
        let _ = self.meshes.insert(name.to_string(), mesh);
    }

    /// Adds a mesh with the specified mesh descriptor and name.
    pub fn add_trimesh(&mut self, descr: TriMesh3<f32>, dynamic_draw: bool, name: &str) -> Rc<RefCell<Mesh>> {
        let mesh = Mesh::from_trimesh(descr, dynamic_draw);
        let mesh = Rc::new(RefCell::new(mesh));

        self.add(mesh.clone(), name);

        mesh
    }

    /// Removes a mesh from this cache.
    pub fn remove(&mut self, name: &str) {
        let _ = self.meshes.remove(&name.to_string());
    }

    // FIXME: is this the right place to put this?
    /// Loads the meshes described by an obj file.
    pub fn load_obj(path: &Path, mtl_dir: &Path, geometry_name: &str)
                    -> IoResult<Vec<(String, Rc<RefCell<Mesh>>, Option<MtlMaterial>)>> {
        obj::parse_file(path, mtl_dir, geometry_name).map(|ms| {
            let mut res = Vec::new();

            for (n, m, mat) in ms.into_iter() {
                let m = Rc::new(RefCell::new(m));

                res.push((n, m, mat));
            }

            res
        })
    }
}
<commit_msg>Fix warnings.<commit_after>//! A resource manager to load meshes.

use std::io::IoResult;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
use ncollide::procedural::TriMesh3;
use ncollide::procedural;
use resource::Mesh;
use loader::obj;
use loader::mtl::MtlMaterial;

local_data_key!(KEY_MESH_MANAGER: RefCell<MeshManager>)

/// The mesh manager.
///
/// Upon construction, it contains:
///
/// It keeps a cache of already-loaded meshes. Note that this is only a cache, nothing more.
/// Thus, its usage is not required to load meshes.
pub struct MeshManager {
    meshes:       HashMap<String, Rc<RefCell<Mesh>>>
}

impl MeshManager {
    /// Creates a new mesh manager.
    pub fn new() -> MeshManager {
        let mut res = MeshManager {
            meshes: HashMap::new()
        };

        let _ = res.add_trimesh(procedural::unit_sphere(50, 50, true), false, "sphere");
        let _ = res.add_trimesh(procedural::unit_cuboid(), false, "cube");
        let _ = res.add_trimesh(procedural::unit_cone(50), false, "cone");
        let _ = res.add_trimesh(procedural::unit_cylinder(50), false, "cylinder");

        res
    }

    /// Mutably applies a function to the mesh manager.
    pub fn get_global_manager<T>(f: |&mut MeshManager| -> T) -> T {
        if KEY_MESH_MANAGER.get().is_none() {
            let _ = KEY_MESH_MANAGER.replace(Some(RefCell::new(MeshManager::new())));
        }

        f(KEY_MESH_MANAGER.get().unwrap().borrow_mut().deref_mut())
    }

    /// Get a mesh with the specified name. Returns `None` if the mesh is not registered.
    pub fn get(&mut self, name: &str) -> Option<Rc<RefCell<Mesh>>> {
        self.meshes.get(&name.to_string()).map(|t| t.clone())
    }

    /// Adds a mesh with the specified name to this cache.
    pub fn add(&mut self, mesh: Rc<RefCell<Mesh>>, name: &str) {
        let _ = self.meshes.insert(name.to_string(), mesh);
    }

    /// Adds a mesh with the specified mesh descriptor and name.
    pub fn add_trimesh(&mut self, descr: TriMesh3<f32>, dynamic_draw: bool, name: &str) -> Rc<RefCell<Mesh>> {
        let mesh = Mesh::from_trimesh(descr, dynamic_draw);
        let mesh = Rc::new(RefCell::new(mesh));

        self.add(mesh.clone(), name);

        mesh
    }

    /// Removes a mesh from this cache.
    pub fn remove(&mut self, name: &str) {
        let _ = self.meshes.remove(&name.to_string());
    }

    // FIXME: is this the right place to put this?
    /// Loads the meshes described by an obj file.
    pub fn load_obj(path: &Path, mtl_dir: &Path, geometry_name: &str)
                    -> IoResult<Vec<(String, Rc<RefCell<Mesh>>, Option<MtlMaterial>)>> {
        obj::parse_file(path, mtl_dir, geometry_name).map(|ms| {
            let mut res = Vec::new();

            for (n, m, mat) in ms.into_iter() {
                let m = Rc::new(RefCell::new(m));

                res.push((n, m, mat));
            }

            res
        })
    }
}
<|endoftext|>


<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 crate::node_identifier::NodeIdentifier;
use crate::util::{is_relay_custom_inline_fragment_directive, PointerAddress};
use crate::DEFER_STREAM_CONSTANTS;

use common::NamedItem;
use dashmap::DashMap;
use graphql_ir::{
    Condition, FragmentDefinition, InlineFragment, LinkedField, OperationDefinition, Program,
    Selection, Transformed, TransformedValue,
};
use rayon::prelude::*;
use schema::SDLSchema;
use std::iter::Iterator;
use std::sync::Arc;

/**
 * A transform that removes redundant fields and fragment spreads. Redundancy is
 * defined in this context as any selection that is guaranteed to already be
 * fetched by an ancestor selection. This can occur in two cases:
 *
 * 1. Simple duplicates at the same level of the document can always be skipped:
 *
 *
 * fragment Foo on FooType {
 *   id
 *   id
 *   ...Bar
 *   ...Bar
 * }
 *
 *
 * Becomes
 *
 *
 * fragment Foo on FooType {
 *   id
 *   ...Bar
 * }
 *
 *
 * 2. Inline fragments and conditions introduce the possibility for duplication
 * at different levels of the tree. Whenever a selection is fetched in a parent,
 * it is redundant to also fetch it in a child:
 *
 *
 * fragment Foo on FooType {
 *   id
 *   ... on OtherType {
 *     id # 1
 *   }
 *   ... on FooType @include(if: $cond) {
 *     id # 2
 *   }
 * }
 *
 *
 * Becomes:
 *
 *
 * fragment Foo on FooType {
 *   id
 * }
 *
 *
 * In this example:
 * - 1 can be skipped because `id` is already fetched by the parent. Even
 *   though the type is different (FooType/OtherType), the inline fragment
 *   cannot match without the outer fragment matching so the outer `id` is
 *   guaranteed to already be fetched.
 * - 2 can be skipped for similar reasons: it doesn't matter if the condition
 *   holds, `id` is already fetched by the parent regardless.
 *
 * This transform also handles more complicated cases in which selections are
 * nested:
 *
 *
 * fragment Foo on FooType {
 *   a {
 *     bb
 *   }
 *   ... on OtherType {
 *     a {
 *       bb # 1
 *       cc
 *     }
 *   }
 *  }
 *
 *
 * Becomes
 *
 *
 * fragment Foo on FooType {
 *   a {
 *     bb
 *   }
 *   ... on OtherType {
 *     a {
 *       cc
 *     }
 *   }
 *  }
 *
 *
 * 1 can be skipped because it is already fetched at the outer level.
 */
pub fn skip_redundant_nodes(program: &Program) -> Program {
    let transform = SkipRedundantNodesTransform::new(program);
    transform
        .transform_program(program)
        .replace_or_else(|| program.clone())
}

#[derive(Default, Clone, Debug)]
struct SelectionMap(VecMap<NodeIdentifier, Option<SelectionMap>>);

type Cache = DashMap<PointerAddress, (Transformed<Selection>, SelectionMap)>;

struct SkipRedundantNodesTransform {
    schema: Arc<SDLSchema>,
    cache: Cache,
}

impl<'s> SkipRedundantNodesTransform {
    fn new(program: &'_ Program) -> Self {
        Self {
            schema: Arc::clone(&program.schema),
            cache: DashMap::new(),
        }
    }

    fn transform_selection(
        &self,
        selection: &Selection,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Selection> {
        // This will optimize a traversal of the same subselections.
        // If it's the same node, and selection_map is empty
        // result of transform_selection has to be the same.
        let is_empty = selection_map.0.is_empty();
        let identifier = NodeIdentifier::from_selection(&self.schema, selection);
        match selection {
            Selection::ScalarField(_) | Selection::FragmentSpread(_) => {
                if selection_map.0.contains_key(&identifier) {
                    Transformed::Delete
                } else {
                    selection_map.0.insert(identifier, None);
                    Transformed::Keep
                }
            }
            Selection::LinkedField(selection) => {
                let should_cache = is_empty && Arc::strong_count(selection) > 1;
                if should_cache {
                    let key = PointerAddress::new(selection);
                    if let Some(cached) = self.cache.get(&key) {
                        let (cached_result, cached_selection_map) = cached.clone();
                        *selection_map = cached_selection_map;
                        return cached_result;
                    }
                }
                let result = if let Some(Some(linked_selection_map)) =
                    selection_map.0.get_mut(&identifier)
                {
                    self.transform_linked_field(selection, linked_selection_map)
                        .map(Selection::LinkedField)
                } else {
                    let mut linked_selection_map = Default::default();
                    let result = self
                        .transform_linked_field(selection, &mut linked_selection_map)
                        .map(Selection::LinkedField);
                    if !matches!(result, Transformed::Delete) {
                        selection_map
                            .0
                            .insert(identifier, Some(linked_selection_map));
                    }
                    result
                };
                if should_cache {
                    let key = PointerAddress::new(selection);
                    self.cache
                        .insert(key, (result.clone(), selection_map.clone()));
                }
                result
            }
            Selection::Condition(selection) => {
                if let Some(Some(existing_selection_map)) = selection_map.0.get_mut(&identifier) {
                    self.transform_condition(selection, existing_selection_map)
                        .map(Selection::Condition)
                } else {
                    // Fork the selection map to prevent conditional selections from
                    // affecting the outer "guaranteed" selections.
                    let mut next_selection_map = selection_map.clone();
                    let result = self
                        .transform_condition(selection, &mut next_selection_map)
                        .map(Selection::Condition);
                    if !matches!(result, Transformed::Delete) {
                        selection_map.0.insert(identifier, Some(next_selection_map));
                    }
                    result
                }
            }
            Selection::InlineFragment(selection) => {
                let should_cache = is_empty && Arc::strong_count(selection) > 1;
                if should_cache {
                    let key = PointerAddress::new(selection);
                    if let Some(cached) = self.cache.get(&key) {
                        let (cached_result, cached_selection_map) = cached.clone();
                        *selection_map = cached_selection_map;
                        return cached_result;
                    }
                }
                let result = if let Some(Some(existing_selection_map)) =
                    selection_map.0.get_mut(&identifier)
                {
                    self.transform_inline_fragment(selection, existing_selection_map)
                        .map(Selection::InlineFragment)
                } else if selection
                    .directives
                    .iter()
                    .any(is_relay_custom_inline_fragment_directive)
                {
                    let mut linked_selection_map = Default::default();
                    let result = self
                        .transform_inline_fragment(selection, &mut linked_selection_map)
                        .map(Selection::InlineFragment);
                    if !matches!(result, Transformed::Delete) {
                        selection_map
                            .0
                            .insert(identifier, Some(linked_selection_map));
                    }
                    result
                } else {
                    // Fork for inline fragments for the same reason
                    let mut next_selection_map = selection_map.clone();
                    let result = self
                        .transform_inline_fragment(selection, &mut next_selection_map)
                        .map(Selection::InlineFragment);
                    if !matches!(result, Transformed::Delete) {
                        selection_map.0.insert(identifier, Some(next_selection_map));
                    }
                    result
                };
                if should_cache {
                    let key = PointerAddress::new(selection);
                    self.cache
                        .insert(key, (result.clone(), selection_map.clone()));
                }
                result
            }
        }
    }

    fn transform_linked_field(
        &self,
        field: &LinkedField,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Arc<LinkedField>> {
        let selections = self.transform_selections(&field.selections, selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => {
                if selections.is_empty() {
                    Transformed::Delete
                } else {
                    Transformed::Replace(Arc::new(LinkedField {
                        selections,
                        ..field.clone()
                    }))
                }
            }
        }
    }

    fn transform_condition(
        &self,
        condition: &Condition,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Arc<Condition>> {
        let selections = self.transform_selections(&condition.selections, selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => {
                if selections.is_empty() {
                    Transformed::Delete
                } else {
                    Transformed::Replace(Arc::new(Condition {
                        selections,
                        ..condition.clone()
                    }))
                }
            }
        }
    }

    fn transform_inline_fragment(
        &self,
        fragment: &InlineFragment,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Arc<InlineFragment>> {
        let selections = self.transform_selections(&fragment.selections, selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => {
                if selections.is_empty() {
                    Transformed::Delete
                } else {
                    Transformed::Replace(Arc::new(InlineFragment {
                        selections,
                        ..fragment.clone()
                    }))
                }
            }
        }
    }

    // Mostly a copy from Transformer::transform_list, but does partition and pass down `selection_map`.
    fn transform_selections(
        &self,
        selections: &[Selection],
        selection_map: &mut SelectionMap,
    ) -> TransformedValue<Vec<Selection>> {
        if selections.is_empty() {
            return TransformedValue::Keep;
        }
        let mut result: Vec<Selection> = Vec::new();
        let mut has_changes = false;
        let selections = get_partitioned_selections(selections);

        for (index, prev_item) in selections.iter().enumerate() {
            let next_item = self.transform_selection(prev_item, selection_map);
            match next_item {
                Transformed::Keep => {
                    if has_changes {
                        result.push((*prev_item).clone());
                    }
                }
                Transformed::Delete => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(selections.len());
                        result.extend(selections.iter().take(index).map(|&x| x.clone()));
                        has_changes = true;
                    }
                }
                Transformed::Replace(next_item) => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(selections.len());
                        result.extend(selections.iter().take(index).map(|&x| x.clone()));
                        has_changes = true;
                    }
                    result.push(next_item);
                }
            }
        }
        if has_changes {
            TransformedValue::Replace(result)
        } else {
            TransformedValue::Replace(selections.iter().map(|&x| x.clone()).collect())
        }
    }

    fn transform_operation(
        &self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        let mut selection_map = Default::default();
        let selections = self.transform_selections(&operation.selections, &mut selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => Transformed::Replace(OperationDefinition {
                selections,
                ..operation.clone()
            }),
        }
    }

    fn transform_fragment(&self, fragment: &FragmentDefinition) -> Transformed<FragmentDefinition> {
        let mut selection_map = Default::default();
        let selections = self.transform_selections(&fragment.selections, &mut selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => Transformed::Replace(FragmentDefinition {
                selections,
                ..fragment.clone()
            }),
        }
    }

    fn transform_program(&self, program: &Program) -> TransformedValue<Program> {
        let operations: Vec<Arc<OperationDefinition>> = program
            .par_operations()
            .filter_map(|operation| match self.transform_operation(operation) {
                Transformed::Delete => None,
                Transformed::Keep => Some(Arc::clone(operation)),
                Transformed::Replace(replacement) => Some(Arc::new(replacement)),
            })
            .collect();
        let fragments: Vec<Arc<FragmentDefinition>> = program
            .par_fragments()
            .filter_map(|fragment| match self.transform_fragment(fragment) {
                Transformed::Delete => None,
                Transformed::Keep => Some(Arc::clone(fragment)),
                Transformed::Replace(replacement) => Some(Arc::new(replacement)),
            })
            .collect();
        let mut next_program = Program::new(Arc::clone(&program.schema));
        for operation in operations {
            next_program.insert_operation(operation);
        }
        for fragment in fragments {
            next_program.insert_fragment(fragment);
        }
        TransformedValue::Replace(next_program)
    }
}

/* Selections are sorted with fields first, "conditionals"
 * (inline fragments & conditions) last. This means that all fields that are
 * guaranteed to be fetched are encountered prior to any duplicates that may be
 * fetched within a conditional.
 */
fn get_partitioned_selections(selections: &[Selection]) -> Vec<&Selection> {
    let mut result = Vec::with_capacity(selections.len());
    unsafe {
        result.set_len(selections.len())
    };
    let mut non_field_index = selections
        .iter()
        .filter(|sel| is_selection_linked_or_scalar(sel))
        .count();
    let mut field_index = 0;
    for sel in selections.iter() {
        if is_selection_linked_or_scalar(sel) {
            result[field_index] = sel;
            field_index += 1;
        } else {
            result[non_field_index] = sel;
            non_field_index += 1;
        }
    }
    result
}

fn is_selection_linked_or_scalar(selection: &Selection) -> bool {
    match selection {
        Selection::LinkedField(field) => field
            .directives
            .named(DEFER_STREAM_CONSTANTS.stream_name)
            .is_none(),
        Selection::ScalarField(_) => true,
        _ => false,
    }
}

/// NOTE: intentionally local to this file, this is not a fully-general purpose
/// immutable map. see comments on methods below.
#[derive(Debug)]
struct VecMap<K, V> {
    data: Arc<Vec<(K, V)>>,
}

impl<K, V> VecMap<K, V> {
    fn new() -> Self {
        Self {
            data: Arc::new(Vec::new()),
        }
    }
}

impl<K, V> VecMap<K, V>
where
    K: Eq + Clone,
    V: Clone,
{
    fn contains_key(&self, key: &K) -> bool {
        self.data.iter().any(|(k, _v)| k == key)
    }

    fn get_mut(&mut self, key: &K) -> Option<&mut V> {
        let data = Arc::make_mut(&mut self.data);
        data.iter_mut().find(|(k, _v)| k == key).map(|(_k, v)| v)
    }

    fn insert(&mut self, key: K, value: V) {
        // NOTE: this is intentionally *not* a general-purpose insert, which should
        // update the value for the existig key if present. skip_redundant_nodes
        // always checks for the key first, so any call to insert() is guaranteed to
        // be for a non-present key. thanks to that we can bypass the existence check
        // to make insert faster.
        debug_assert!(!self.contains_key(&key));
        let data = Arc::make_mut(&mut self.data);
        data.push((key, value));
    }

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

impl<K, V> Clone for VecMap<K, V> {
    fn clone(&self) -> Self {
        Self {
            data: Arc::clone(&self.data),
        }
    }
}

impl<K, V> Default for VecMap<K, V> {
    fn default() -> Self {
        Self::new()
    }
}
<commit_msg>skip_redundant_nodes: Cache less selections<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 crate::node_identifier::NodeIdentifier;
use crate::util::{is_relay_custom_inline_fragment_directive, PointerAddress};
use crate::DEFER_STREAM_CONSTANTS;

use common::NamedItem;
use dashmap::DashMap;
use graphql_ir::{
    Condition, FragmentDefinition, InlineFragment, LinkedField, OperationDefinition, Program,
    Selection, Transformed, TransformedValue,
};
use rayon::prelude::*;
use schema::SDLSchema;
use std::iter::Iterator;
use std::sync::Arc;

/**
 * A transform that removes redundant fields and fragment spreads. Redundancy is
 * defined in this context as any selection that is guaranteed to already be
 * fetched by an ancestor selection. This can occur in two cases:
 *
 * 1. Simple duplicates at the same level of the document can always be skipped:
 *
 *
 * fragment Foo on FooType {
 *   id
 *   id
 *   ...Bar
 *   ...Bar
 * }
 *
 *
 * Becomes
 *
 *
 * fragment Foo on FooType {
 *   id
 *   ...Bar
 * }
 *
 *
 * 2. Inline fragments and conditions introduce the possibility for duplication
 * at different levels of the tree. Whenever a selection is fetched in a parent,
 * it is redundant to also fetch it in a child:
 *
 *
 * fragment Foo on FooType {
 *   id
 *   ... on OtherType {
 *     id # 1
 *   }
 *   ... on FooType @include(if: $cond) {
 *     id # 2
 *   }
 * }
 *
 *
 * Becomes:
 *
 *
 * fragment Foo on FooType {
 *   id
 * }
 *
 *
 * In this example:
 * - 1 can be skipped because `id` is already fetched by the parent. Even
 *   though the type is different (FooType/OtherType), the inline fragment
 *   cannot match without the outer fragment matching so the outer `id` is
 *   guaranteed to already be fetched.
 * - 2 can be skipped for similar reasons: it doesn't matter if the condition
 *   holds, `id` is already fetched by the parent regardless.
 *
 * This transform also handles more complicated cases in which selections are
 * nested:
 *
 *
 * fragment Foo on FooType {
 *   a {
 *     bb
 *   }
 *   ... on OtherType {
 *     a {
 *       bb # 1
 *       cc
 *     }
 *   }
 *  }
 *
 *
 * Becomes
 *
 *
 * fragment Foo on FooType {
 *   a {
 *     bb
 *   }
 *   ... on OtherType {
 *     a {
 *       cc
 *     }
 *   }
 *  }
 *
 *
 * 1 can be skipped because it is already fetched at the outer level.
 */
pub fn skip_redundant_nodes(program: &Program) -> Program {
    let transform = SkipRedundantNodesTransform::new(program);
    transform
        .transform_program(program)
        .replace_or_else(|| program.clone())
}

#[derive(Default, Clone, Debug)]
struct SelectionMap(VecMap<NodeIdentifier, Option<SelectionMap>>);

type Cache = DashMap<PointerAddress, (Transformed<Selection>, SelectionMap)>;

struct SkipRedundantNodesTransform {
    schema: Arc<SDLSchema>,
    cache: Cache,
}

impl<'s> SkipRedundantNodesTransform {
    fn new(program: &'_ Program) -> Self {
        Self {
            schema: Arc::clone(&program.schema),
            cache: DashMap::new(),
        }
    }

    fn transform_selection(
        &self,
        selection: &Selection,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Selection> {
        // This will optimize a traversal of the same subselections.
        // If it's the same node, and selection_map is empty
        // result of transform_selection has to be the same.
        let is_empty = selection_map.0.is_empty();
        let identifier = NodeIdentifier::from_selection(&self.schema, selection);
        match selection {
            Selection::ScalarField(_) | Selection::FragmentSpread(_) => {
                if selection_map.0.contains_key(&identifier) {
                    Transformed::Delete
                } else {
                    selection_map.0.insert(identifier, None);
                    Transformed::Keep
                }
            }
            Selection::LinkedField(selection) => {
                let should_cache = is_empty && Arc::strong_count(selection) > 2;
                if should_cache {
                    let key = PointerAddress::new(selection);
                    if let Some(cached) = self.cache.get(&key) {
                        let (cached_result, cached_selection_map) = cached.clone();
                        *selection_map = cached_selection_map;
                        return cached_result;
                    }
                }
                let result = if let Some(Some(linked_selection_map)) =
                    selection_map.0.get_mut(&identifier)
                {
                    self.transform_linked_field(selection, linked_selection_map)
                        .map(Selection::LinkedField)
                } else {
                    let mut linked_selection_map = Default::default();
                    let result = self
                        .transform_linked_field(selection, &mut linked_selection_map)
                        .map(Selection::LinkedField);
                    if !matches!(result, Transformed::Delete) {
                        selection_map
                            .0
                            .insert(identifier, Some(linked_selection_map));
                    }
                    result
                };
                if should_cache {
                    let key = PointerAddress::new(selection);
                    self.cache
                        .insert(key, (result.clone(), selection_map.clone()));
                }
                result
            }
            Selection::Condition(selection) => {
                if let Some(Some(existing_selection_map)) = selection_map.0.get_mut(&identifier) {
                    self.transform_condition(selection, existing_selection_map)
                        .map(Selection::Condition)
                } else {
                    // Fork the selection map to prevent conditional selections from
                    // affecting the outer "guaranteed" selections.
                    let mut next_selection_map = selection_map.clone();
                    let result = self
                        .transform_condition(selection, &mut next_selection_map)
                        .map(Selection::Condition);
                    if !matches!(result, Transformed::Delete) {
                        selection_map.0.insert(identifier, Some(next_selection_map));
                    }
                    result
                }
            }
            Selection::InlineFragment(selection) => {
                let should_cache = is_empty && Arc::strong_count(selection) > 2;
                if should_cache {
                    let key = PointerAddress::new(selection);
                    if let Some(cached) = self.cache.get(&key) {
                        let (cached_result, cached_selection_map) = cached.clone();
                        *selection_map = cached_selection_map;
                        return cached_result;
                    }
                }
                let result = if let Some(Some(existing_selection_map)) =
                    selection_map.0.get_mut(&identifier)
                {
                    self.transform_inline_fragment(selection, existing_selection_map)
                        .map(Selection::InlineFragment)
                } else if selection
                    .directives
                    .iter()
                    .any(is_relay_custom_inline_fragment_directive)
                {
                    let mut linked_selection_map = Default::default();
                    let result = self
                        .transform_inline_fragment(selection, &mut linked_selection_map)
                        .map(Selection::InlineFragment);
                    if !matches!(result, Transformed::Delete) {
                        selection_map
                            .0
                            .insert(identifier, Some(linked_selection_map));
                    }
                    result
                } else {
                    // Fork for inline fragments for the same reason
                    let mut next_selection_map = selection_map.clone();
                    let result = self
                        .transform_inline_fragment(selection, &mut next_selection_map)
                        .map(Selection::InlineFragment);
                    if !matches!(result, Transformed::Delete) {
                        selection_map.0.insert(identifier, Some(next_selection_map));
                    }
                    result
                };
                if should_cache {
                    let key = PointerAddress::new(selection);
                    self.cache
                        .insert(key, (result.clone(), selection_map.clone()));
                }
                result
            }
        }
    }

    fn transform_linked_field(
        &self,
        field: &LinkedField,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Arc<LinkedField>> {
        let selections = self.transform_selections(&field.selections, selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => {
                if selections.is_empty() {
                    Transformed::Delete
                } else {
                    Transformed::Replace(Arc::new(LinkedField {
                        selections,
                        ..field.clone()
                    }))
                }
            }
        }
    }

    fn transform_condition(
        &self,
        condition: &Condition,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Arc<Condition>> {
        let selections = self.transform_selections(&condition.selections, selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => {
                if selections.is_empty() {
                    Transformed::Delete
                } else {
                    Transformed::Replace(Arc::new(Condition {
                        selections,
                        ..condition.clone()
                    }))
                }
            }
        }
    }

    fn transform_inline_fragment(
        &self,
        fragment: &InlineFragment,
        selection_map: &mut SelectionMap,
    ) -> Transformed<Arc<InlineFragment>> {
        let selections = self.transform_selections(&fragment.selections, selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => {
                if selections.is_empty() {
                    Transformed::Delete
                } else {
                    Transformed::Replace(Arc::new(InlineFragment {
                        selections,
                        ..fragment.clone()
                    }))
                }
            }
        }
    }

    // Mostly a copy from Transformer::transform_list, but does partition and pass down `selection_map`.
    fn transform_selections(
        &self,
        selections: &[Selection],
        selection_map: &mut SelectionMap,
    ) -> TransformedValue<Vec<Selection>> {
        if selections.is_empty() {
            return TransformedValue::Keep;
        }
        let mut result: Vec<Selection> = Vec::new();
        let mut has_changes = false;
        let selections = get_partitioned_selections(selections);

        for (index, prev_item) in selections.iter().enumerate() {
            let next_item = self.transform_selection(prev_item, selection_map);
            match next_item {
                Transformed::Keep => {
                    if has_changes {
                        result.push((*prev_item).clone());
                    }
                }
                Transformed::Delete => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(selections.len());
                        result.extend(selections.iter().take(index).map(|&x| x.clone()));
                        has_changes = true;
                    }
                }
                Transformed::Replace(next_item) => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(selections.len());
                        result.extend(selections.iter().take(index).map(|&x| x.clone()));
                        has_changes = true;
                    }
                    result.push(next_item);
                }
            }
        }
        if has_changes {
            TransformedValue::Replace(result)
        } else {
            TransformedValue::Replace(selections.iter().map(|&x| x.clone()).collect())
        }
    }

    fn transform_operation(
        &self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        let mut selection_map = Default::default();
        let selections = self.transform_selections(&operation.selections, &mut selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => Transformed::Replace(OperationDefinition {
                selections,
                ..operation.clone()
            }),
        }
    }

    fn transform_fragment(&self, fragment: &FragmentDefinition) -> Transformed<FragmentDefinition> {
        let mut selection_map = Default::default();
        let selections = self.transform_selections(&fragment.selections, &mut selection_map);
        match selections {
            TransformedValue::Keep => Transformed::Keep,
            TransformedValue::Replace(selections) => Transformed::Replace(FragmentDefinition {
                selections,
                ..fragment.clone()
            }),
        }
    }

    fn transform_program(&self, program: &Program) -> TransformedValue<Program> {
        let operations: Vec<Arc<OperationDefinition>> = program
            .par_operations()
            .filter_map(|operation| match self.transform_operation(operation) {
                Transformed::Delete => None,
                Transformed::Keep => Some(Arc::clone(operation)),
                Transformed::Replace(replacement) => Some(Arc::new(replacement)),
            })
            .collect();
        let fragments: Vec<Arc<FragmentDefinition>> = program
            .par_fragments()
            .filter_map(|fragment| match self.transform_fragment(fragment) {
                Transformed::Delete => None,
                Transformed::Keep => Some(Arc::clone(fragment)),
                Transformed::Replace(replacement) => Some(Arc::new(replacement)),
            })
            .collect();
        let mut next_program = Program::new(Arc::clone(&program.schema));
        for operation in operations {
            next_program.insert_operation(operation);
        }
        for fragment in fragments {
            next_program.insert_fragment(fragment);
        }
        TransformedValue::Replace(next_program)
    }
}

/* Selections are sorted with fields first, "conditionals"
 * (inline fragments & conditions) last. This means that all fields that are
 * guaranteed to be fetched are encountered prior to any duplicates that may be
 * fetched within a conditional.
 */
fn get_partitioned_selections(selections: &[Selection]) -> Vec<&Selection> {
    let mut result = Vec::with_capacity(selections.len());
    unsafe {
        result.set_len(selections.len())
    };
    let mut non_field_index = selections
        .iter()
        .filter(|sel| is_selection_linked_or_scalar(sel))
        .count();
    let mut field_index = 0;
    for sel in selections.iter() {
        if is_selection_linked_or_scalar(sel) {
            result[field_index] = sel;
            field_index += 1;
        } else {
            result[non_field_index] = sel;
            non_field_index += 1;
        }
    }
    result
}

fn is_selection_linked_or_scalar(selection: &Selection) -> bool {
    match selection {
        Selection::LinkedField(field) => field
            .directives
            .named(DEFER_STREAM_CONSTANTS.stream_name)
            .is_none(),
        Selection::ScalarField(_) => true,
        _ => false,
    }
}

/// NOTE: intentionally local to this file, this is not a fully-general purpose
/// immutable map. see comments on methods below.
#[derive(Debug)]
struct VecMap<K, V> {
    data: Arc<Vec<(K, V)>>,
}

impl<K, V> VecMap<K, V> {
    fn new() -> Self {
        Self {
            data: Arc::new(Vec::new()),
        }
    }
}

impl<K, V> VecMap<K, V>
where
    K: Eq + Clone,
    V: Clone,
{
    fn contains_key(&self, key: &K) -> bool {
        self.data.iter().any(|(k, _v)| k == key)
    }

    fn get_mut(&mut self, key: &K) -> Option<&mut V> {
        let data = Arc::make_mut(&mut self.data);
        data.iter_mut().find(|(k, _v)| k == key).map(|(_k, v)| v)
    }

    fn insert(&mut self, key: K, value: V) {
        // NOTE: this is intentionally *not* a general-purpose insert, which should
        // update the value for the existig key if present. skip_redundant_nodes
        // always checks for the key first, so any call to insert() is guaranteed to
        // be for a non-present key. thanks to that we can bypass the existence check
        // to make insert faster.
        debug_assert!(!self.contains_key(&key));
        let data = Arc::make_mut(&mut self.data);
        data.push((key, value));
    }

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

impl<K, V> Clone for VecMap<K, V> {
    fn clone(&self) -> Self {
        Self {
            data: Arc::clone(&self.data),
        }
    }
}

impl<K, V> Default for VecMap<K, V> {
    fn default() -> Self {
        Self::new()
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::codegen::Bindings::FileBinding;
use dom::blob::{Blob, BlobType, FileTypeId};
use dom::window::Window;
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct File {
    pub blob: Blob,
    pub name: DOMString,
    pub window: JS<Window>,
    pub type_: BlobType
}

impl File {
    pub fn new_inherited(window: &JSRef<Window>, _file_bits: &JSRef<Blob>, name: DOMString) -> File {
        File {
            blob: Blob::new_inherited(window),
            name: name,
            window: JS::from_rooted(window),
            type_: FileTypeId
        }
        // XXXManishearth Once Blob is able to store data
        // the relevant subfields of file_bits should be copied over
    }

    pub fn new(window: &JSRef<Window>, file_bits: &JSRef<Blob>, name: DOMString) -> Temporary<File> {
        reflect_dom_object(box File::new_inherited(window, file_bits, name),
                           window,
                           FileBinding::Wrap)
    }
}

pub trait FileMethods {
    fn Name(&self) -> DOMString;
}

impl FileMethods for File {
    fn Name(&self) -> DOMString {
        self.name.clone()
    }
}

impl Reflectable for File {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.blob.reflector()
    }
}
<commit_msg>Remove the unused window field from File.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::codegen::Bindings::FileBinding;
use dom::blob::{Blob, BlobType, FileTypeId};
use dom::window::Window;
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct File {
    pub blob: Blob,
    pub name: DOMString,
    pub type_: BlobType
}

impl File {
    pub fn new_inherited(window: &JSRef<Window>, _file_bits: &JSRef<Blob>, name: DOMString) -> File {
        File {
            blob: Blob::new_inherited(window),
            name: name,
            type_: FileTypeId
        }
        // XXXManishearth Once Blob is able to store data
        // the relevant subfields of file_bits should be copied over
    }

    pub fn new(window: &JSRef<Window>, file_bits: &JSRef<Blob>, name: DOMString) -> Temporary<File> {
        reflect_dom_object(box File::new_inherited(window, file_bits, name),
                           window,
                           FileBinding::Wrap)
    }
}

pub trait FileMethods {
    fn Name(&self) -> DOMString;
}

impl FileMethods for File {
    fn Name(&self) -> DOMString {
        self.name.clone()
    }
}

impl Reflectable for File {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.blob.reflector()
    }
}
<|endoftext|>


<commit_before>use clap::ArgMatches;
use std::collections::HashSet;

pub fn actions_get(matches: &ArgMatches) -> HashSet<String> {
    let mut hs_actions = HashSet::<String>::new();

    if let Some(_) = matches.values_of("list-provides") {
        let bill = String::from("load-jobs");
        hs_actions.insert(bill);
        let list_provides = String::from("list-provides");
        hs_actions.insert(list_provides);
    }
    if let Some(_) = matches.values_of("list-target") {
        let bill = String::from("load-jobs");
        hs_actions.insert(bill);
        let list_provides = String::from("list-target");
        hs_actions.insert(list_provides);
    }
    if let Some(_) = matches.values_of("session") {
        let bill = String::from("load-jobs");
        hs_actions.insert(bill);
        let str_load_scripts = String::from("load-scripts");
        hs_actions.insert(str_load_scripts);
        let session = String::from("session");
        hs_actions.insert(session);
    }
    return hs_actions;
}
<commit_msg>Apply rustfmt<commit_after>use clap::ArgMatches;
use std::collections::HashSet;

pub fn actions_get(matches: &ArgMatches) -> HashSet<String> {
    let mut hs_actions = HashSet::<String>::new();

    if let Some(_) = matches.values_of("list-provides") {
        let bill = String::from("load-jobs");
        hs_actions.insert(bill);
        let list_provides = String::from("list-provides");
        hs_actions.insert(list_provides);
    }
    if let Some(_) = matches.values_of("list-target") {
        let bill = String::from("load-jobs");
        hs_actions.insert(bill);
        let list_provides = String::from("list-target");
        hs_actions.insert(list_provides);
    }
    if let Some(_) = matches.values_of("session") {
        let bill = String::from("load-jobs");
        hs_actions.insert(bill);
        let str_load_scripts = String::from("load-scripts");
        hs_actions.insert(str_load_scripts);
        let session = String::from("session");
        hs_actions.insert(session);
    }
    return hs_actions;
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]

extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn_mid::ItemFn;

/// An attribute for easy generation of a const function with conditional compilations.
#[proc_macro_attribute]
pub fn const_fn(args: TokenStream, function: TokenStream) -> TokenStream {
    assert!(!args.is_empty(), "requires an argument");

    let const_function = syn::parse_macro_input!(function as ItemFn);

    if const_function.sig.constness.is_none() {
        return syn::Error::new_spanned(
            const_function.sig.fn_token,
            "#[const_fn] attribute may only be used on const functions",
        )
        .to_compile_error()
        .into();
    }

    let mut function = const_function.clone();
    function.sig.constness = None;

    let args = TokenStream2::from(args);
    TokenStream::from(quote! {
        #[cfg(not(#args))]
        #function
        #[cfg(#args)]
        #const_function
    })
}
<commit_msg>Fix warnings<commit_after>#![warn(rust_2018_idioms)]

// older compilers require explicit `extern crate`.
#[allow(unused_extern_crates)]
extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn_mid::ItemFn;

/// An attribute for easy generation of a const function with conditional compilations.
#[proc_macro_attribute]
pub fn const_fn(args: TokenStream, function: TokenStream) -> TokenStream {
    assert!(!args.is_empty(), "requires an argument");

    let const_function = syn::parse_macro_input!(function as ItemFn);

    if const_function.sig.constness.is_none() {
        return syn::Error::new_spanned(
            const_function.sig.fn_token,
            "#[const_fn] attribute may only be used on const functions",
        )
        .to_compile_error()
        .into();
    }

    let mut function = const_function.clone();
    function.sig.constness = None;

    let args = TokenStream2::from(args);
    TokenStream::from(quote! {
        #[cfg(not(#args))]
        #function
        #[cfg(#args)]
        #const_function
    })
}
<|endoftext|>


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

extern crate libc;

#[macro_use]
extern crate apache2;

#[macro_use]
extern crate lazy_static;

use std::mem;
use libc::{c_void, c_char};
use apache2::{Request, Status, Server, ffi};
use apache2::wrapper::{Wrapper, from_char_ptr};

macro_rules! config_struct {
   ($name:ident { $( $fieldname:ident: $ctype:ty ),* }) => {
      #[repr(C)]
      interpolate_idents! {
         struct [$name C] {
            $(
               pub $fieldname: $ctype,
            )*
         }

         type $name<'a> = apache2::wrapper::Wrapper<'a, [$name C]>;
      }
   }
}

config_struct!(
   ExampleConfig {
      first_cmd: *const c_char,
      second_cmd: *const c_char
   }
);

#[allow(unused_variables)]
pub extern "C" fn first_cmd(parms: *mut ffi::cmd_parms, p: *mut c_void, w: *const c_char) -> *const c_char {
   let config = unsafe { ffi::ap_get_module_config((*(*parms).server).module_config, &conf_module) as *mut ExampleConfigC };

   unsafe { (*config).first_cmd = w };

   std::ptr::null()
}

#[allow(unused_variables)]
pub extern "C" fn second_cmd(parms: *mut ffi::cmd_parms, p: *mut c_void, w: *const c_char) -> *const c_char {
   let config = unsafe { ffi::ap_get_module_config((*(*parms).server).module_config, &conf_module) as *mut ExampleConfigC };

   unsafe { (*config).second_cmd = w };

   std::ptr::null()
}

#[allow(unused_variables)]
pub extern "C" fn c_create_server_config(p: *mut ffi::apr_pool_t, s: *mut ffi::server_rec) -> *mut c_void {
   let config: *mut ExampleConfigC = unsafe {
      ffi::apr_pcalloc(p, mem::size_of::<ExampleConfigC>() as ffi::apr_size_t) as *mut ExampleConfigC
   };

   config as *mut c_void
}

//fn create_server_config(pool: &mut Pool, _: &Server) -> ExampleConfigC {

//}

apache2_module!(conf, b"mod_conf\0", commands {
   None,
   None,
   Some(c_create_server_config),
   None,
   AP_INIT_TAKE1!(b"FirstCmd\0", first_cmd, apache2::ffi::RSRC_CONF, b"First cmd description\0");
   AP_INIT_TAKE1!(b"SecondCmd\0", second_cmd, apache2::ffi::RSRC_CONF, b"Second cmd description\0")
});

fn unwrap_str<'a>(wrapped: Result<&'a str, ()>) -> &'a str {
   wrapped.unwrap_or("--")
}

fn conf_handler(r: &mut Request) -> Result<Status, ()> {
   if try!(r.handler()) != "conf" {
      return Ok(Status::DECLINED)
   }

   let config = unsafe { ffi::ap_get_module_config(try!(try!(r.server()).module_config()).raw, &conf_module) as *mut ExampleConfigC };

   r.set_content_type("text/plain; charset=utf-8");

   try!(r.write("FIRST CMD: "));

   let first_cmd = unwrap_str(from_char_ptr(unsafe { (*config).first_cmd }));
   try!(r.write(first_cmd));

   try!(r.write("\n"));

   try!(r.write("SECOND CMD: "));

   let second_cmd = unwrap_str(from_char_ptr(unsafe { (*config).second_cmd }));
   try!(r.write(second_cmd));

   try!(r.write("\n"));

   Ok(Status::OK)
}
<commit_msg>The config_struct macro now defining a Rust warpper config struct.<commit_after>#![feature(plugin)]
#![plugin(interpolate_idents)]

extern crate libc;

#[macro_use]
extern crate apache2;

#[macro_use]
extern crate lazy_static;

use std::mem;
use libc::{c_void, c_char};
use apache2::{Request, Status, Server, ffi};
use apache2::wrapper::{Wrapper, from_char_ptr};

type CStringType = *const c_char;
type StringType<'a> = &'a str;

macro_rules! config_struct {
   ($name:ident { $( $field_name:ident: $field_type:ident ),* }) => {
      #[repr(C)]
      interpolate_idents! {
         config_define_c_struct!(
            [C $name] {
               $(
                  $field_name: [C $field_type]
               ),*
            }
         );
      }

      interpolate_idents! {
         struct $name<'a> {
            pub raw: &'a mut [C $name],
            pub pool: *mut ffi::apr_pool_t
         }

         impl<'a> $name<'a> {
            pub fn from_raw_ptr(ptr: *mut [C $name], pool: *mut ffi::apr_pool_t) -> Result<Self, ()> {
               if ptr.is_null() {
                  Err(())
               } else {
                  let raw: &mut [C $name] = unsafe { &mut *ptr };
                  Ok(
                     $name {
                        raw: raw,
                        pool: pool
                     }
                  )
               }
            }

            $(
               config_wrapper_method!($field_name, $field_type);
            )*
         }
      }
   }
}

macro_rules! config_define_c_struct {
   ($c_name:ident { $( $field_name:ident: $field_type:ident ),* } ) => {
      #[repr(C)]
      struct $c_name {
         $(
            pub $field_name: $field_type,
         )*
      }
   }
}

macro_rules! config_wrapper_method {
   ($field_name:ident, StringType) => {
      pub fn $field_name(&self) -> Result<StringType, ()> {
         from_char_ptr(self.raw.$field_name)
      }

      interpolate_idents! {
         pub fn [set_ $field_name](&mut self, value: StringType) {
            self.raw.$field_name = ffi::strdup(self.pool, value);
         }
      }
   }
}

config_struct!(
   ExampleConfig {
      first_cmd: StringType,
      second_cmd: StringType
   }
);

#[allow(unused_variables)]
pub extern "C" fn first_cmd(parms: *mut ffi::cmd_parms, p: *mut c_void, w: *const c_char) -> *const c_char {
   let config = unsafe { ffi::ap_get_module_config((*(*parms).server).module_config, &conf_module) as *mut CExampleConfig };

   let mut example_config = ExampleConfig::from_raw_ptr(config, unsafe { (*parms).pool }).unwrap();

   example_config.set_first_cmd(from_char_ptr(w).unwrap());

   std::ptr::null()
}

#[allow(unused_variables)]
pub extern "C" fn second_cmd(parms: *mut ffi::cmd_parms, p: *mut c_void, w: *const c_char) -> *const c_char {
   let config = unsafe { ffi::ap_get_module_config((*(*parms).server).module_config, &conf_module) as *mut CExampleConfig };

   let mut example_config = ExampleConfig::from_raw_ptr(config, unsafe { (*parms).pool }).unwrap();

   example_config.set_second_cmd(from_char_ptr(w).unwrap());

   std::ptr::null()
}

#[allow(unused_variables)]
pub extern "C" fn c_create_server_config(p: *mut ffi::apr_pool_t, s: *mut ffi::server_rec) -> *mut c_void {
   let config = unsafe {
      ffi::apr_pcalloc(p, mem::size_of::<CExampleConfig>() as ffi::apr_size_t) as *mut CExampleConfig
   };

   config as *mut c_void
}

//fn create_server_config(pool: &mut Pool, _: &Server) -> CExampleConfig {

//}

apache2_module!(conf, b"mod_conf\0", commands {
   None,
   None,
   Some(c_create_server_config),
   None,
   AP_INIT_TAKE1!(b"FirstCmd\0", first_cmd, apache2::ffi::RSRC_CONF, b"First cmd description\0");
   AP_INIT_TAKE1!(b"SecondCmd\0", second_cmd, apache2::ffi::RSRC_CONF, b"Second cmd description\0")
});

fn unwrap_str<'a>(wrapped: Result<&'a str, ()>) -> &'a str {
   wrapped.unwrap_or("--")
}

fn conf_handler(r: &mut Request) -> Result<Status, ()> {
   if try!(r.handler()) != "conf" {
      return Ok(Status::DECLINED)
   }

   let config = unsafe { ffi::ap_get_module_config(try!(try!(r.server()).module_config()).raw, &conf_module) as *mut CExampleConfig };

   r.set_content_type("text/plain; charset=utf-8");

   try!(r.write("FIRST CMD: "));

   let first_cmd = unwrap_str(from_char_ptr(unsafe { (*config).first_cmd }));
   try!(r.write(first_cmd));

   try!(r.write("\n"));

   try!(r.write("SECOND CMD: "));

   let second_cmd = unwrap_str(from_char_ptr(unsafe { (*config).second_cmd }));
   try!(r.write(second_cmd));

   try!(r.write("\n"));

   Ok(Status::OK)
}
<|endoftext|>