<commit_before>//! The type system. We currently use this to infer types for completion, hover
//! information and various assists.

mod autoderef;
pub(crate) mod primitive;
#[cfg(test)]
mod tests;
pub(crate) mod method_resolution;
mod op;
mod lower;
mod infer;

use std::sync::Arc;
use std::{fmt, mem};

use join_to_string::join;

use crate::{Name, AdtDef, type_ref::Mutability};

pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field};
pub(crate) use infer::{infer, InferenceResult, InferTy};

/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
///
/// This should be cheap to clone.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Ty {
    /// The primitive boolean type. Written as `bool`.
    Bool,

    /// The primitive character type; holds a Unicode scalar value
    /// (a non-surrogate code point). Written as `char`.
    Char,

    /// A primitive integer type. For example, `i32`.
    Int(primitive::UncertainIntTy),

    /// A primitive floating-point type. For example, `f64`.
    Float(primitive::UncertainFloatTy),

    /// Structures, enumerations and unions.
    Adt {
        /// The definition of the struct/enum.
        def_id: AdtDef,
        /// The name, for displaying.
        name: Name,
        /// Substitutions for the generic parameters of the type.
        substs: Substs,
    },

    /// The pointee of a string slice. Written as `str`.
    Str,

    /// The pointee of an array slice.  Written as `[T]`.
    Slice(Arc<Ty>),

    // An array with the given length. Written as `[T; n]`.
    Array(Arc<Ty>),

    /// A raw pointer. Written as `*mut T` or `*const T`
    RawPtr(Arc<Ty>, Mutability),

    /// A reference; a pointer with an associated lifetime. Written as
    /// `&'a mut T` or `&'a T`.
    Ref(Arc<Ty>, Mutability),

    /// The anonymous type of a function declaration/definition. Each
    /// function has a unique type, which is output (for a function
    /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
    ///
    /// This includes tuple struct / enum variant constructors as well.
    ///
    /// For example the type of `bar` here:
    ///
    /// ```rust
    /// fn foo() -> i32 { 1 }
    /// let bar = foo; // bar: fn() -> i32 {foo}
    /// ```
    FnDef {
        /// The definition of the function / constructor.
        def: CallableDef,
        /// For display
        name: Name,
        /// Parameters and return type
        sig: Arc<FnSig>,
        /// Substitutions for the generic parameters of the type
        substs: Substs,
    },

    /// A pointer to a function.  Written as `fn() -> i32`.
    ///
    /// For example the type of `bar` here:
    ///
    /// ```rust
    /// fn foo() -> i32 { 1 }
    /// let bar: fn() -> i32 = foo;
    /// ```
    FnPtr(Arc<FnSig>),

    // rustc has a separate type for each function, which just coerces to the
    // above function pointer type. Once we implement generics, we will probably
    // need this as well.

    // A trait, defined with `dyn Trait`.
    // Dynamic(),

    // The anonymous type of a closure. Used to represent the type of
    // `|a| a`.
    // Closure(DefId, ClosureSubsts<'tcx>),

    // The anonymous type of a generator. Used to represent the type of
    // `|a| yield a`.
    // Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability),

    // A type representing the types stored inside a generator.
    // This should only appear in GeneratorInteriors.
    // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>),
    /// The never type `!`.
    Never,

    /// A tuple type.  For example, `(i32, bool)`.
    Tuple(Arc<[Ty]>),

    // The projection of an associated type.  For example,
    // `<T as Trait<..>>::N`.pub
    // Projection(ProjectionTy),

    // Opaque (`impl Trait`) type found in a return type.
    // Opaque(DefId, Substs),
    /// A type parameter; for example, `T` in `fn f<T>(x: T) {}
    Param {
        /// The index of the parameter (starting with parameters from the
        /// surrounding impl, then the current function).
        idx: u32,
        /// The name of the parameter, for displaying.
        name: Name,
    },

    /// A type variable used during type checking. Not to be confused with a
    /// type parameter.
    Infer(InferTy),

    /// A placeholder for a type which could not be computed; this is propagated
    /// to avoid useless error messages. Doubles as a placeholder where type
    /// variables are inserted before type checking, since we want to try to
    /// infer a better type here anyway -- for the IDE use case, we want to try
    /// to infer as much as possible even in the presence of type errors.
    Unknown,
}

/// A list of substitutions for generic parameters.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Substs(Arc<[Ty]>);

impl Substs {
    pub fn empty() -> Substs {
        Substs(Arc::new([]))
    }
}

/// A function signature.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FnSig {
    input: Vec<Ty>,
    output: Ty,
}

impl Ty {
    pub fn unit() -> Self {
        Ty::Tuple(Arc::new([]))
    }

    pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
        match self {
            Ty::Slice(t) | Ty::Array(t) => t.walk(f),
            Ty::RawPtr(t, _) => t.walk(f),
            Ty::Ref(t, _) => t.walk(f),
            Ty::Tuple(ts) => {
                for t in ts.iter() {
                    t.walk(f);
                }
            }
            Ty::FnPtr(sig) => {
                for input in &sig.input {
                    input.walk(f);
                }
                sig.output.walk(f);
            }
            Ty::FnDef { substs, sig, .. } => {
                for input in &sig.input {
                    input.walk(f);
                }
                sig.output.walk(f);
                for t in substs.0.iter() {
                    t.walk(f);
                }
            }
            Ty::Adt { substs, .. } => {
                for t in substs.0.iter() {
                    t.walk(f);
                }
            }
            Ty::Bool
            | Ty::Char
            | Ty::Int(_)
            | Ty::Float(_)
            | Ty::Str
            | Ty::Never
            | Ty::Param { .. }
            | Ty::Infer(_)
            | Ty::Unknown => {}
        }
        f(self);
    }

    fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
        match self {
            Ty::Slice(t) | Ty::Array(t) => Arc::make_mut(t).walk_mut(f),
            Ty::RawPtr(t, _) => Arc::make_mut(t).walk_mut(f),
            Ty::Ref(t, _) => Arc::make_mut(t).walk_mut(f),
            Ty::Tuple(ts) => {
                // Without an Arc::make_mut_slice, we can't avoid the clone here:
                let mut v: Vec<_> = ts.iter().cloned().collect();
                for t in &mut v {
                    t.walk_mut(f);
                }
                *ts = v.into();
            }
            Ty::FnPtr(sig) => {
                let sig_mut = Arc::make_mut(sig);
                for input in &mut sig_mut.input {
                    input.walk_mut(f);
                }
                sig_mut.output.walk_mut(f);
            }
            Ty::FnDef { substs, sig, .. } => {
                let sig_mut = Arc::make_mut(sig);
                for input in &mut sig_mut.input {
                    input.walk_mut(f);
                }
                sig_mut.output.walk_mut(f);
                // Without an Arc::make_mut_slice, we can't avoid the clone here:
                let mut v: Vec<_> = substs.0.iter().cloned().collect();
                for t in &mut v {
                    t.walk_mut(f);
                }
                substs.0 = v.into();
            }
            Ty::Adt { substs, .. } => {
                // Without an Arc::make_mut_slice, we can't avoid the clone here:
                let mut v: Vec<_> = substs.0.iter().cloned().collect();
                for t in &mut v {
                    t.walk_mut(f);
                }
                substs.0 = v.into();
            }
            Ty::Bool
            | Ty::Char
            | Ty::Int(_)
            | Ty::Float(_)
            | Ty::Str
            | Ty::Never
            | Ty::Param { .. }
            | Ty::Infer(_)
            | Ty::Unknown => {}
        }
        f(self);
    }

    fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Ty {
        self.walk_mut(&mut |ty_mut| {
            let ty = mem::replace(ty_mut, Ty::Unknown);
            *ty_mut = f(ty);
        });
        self
    }

    fn builtin_deref(&self) -> Option<Ty> {
        match self {
            Ty::Ref(t, _) => Some(Ty::clone(t)),
            Ty::RawPtr(t, _) => Some(Ty::clone(t)),
            _ => None,
        }
    }

    /// If this is a type with type parameters (an ADT or function), replaces
    /// the `Substs` for these type parameters with the given ones. (So e.g. if
    /// `self` is `Option<_>` and the substs contain `u32`, we'll have
    /// `Option<u32>` afterwards.)
    pub fn apply_substs(self, substs: Substs) -> Ty {
        match self {
            Ty::Adt { def_id, name, .. } => Ty::Adt { def_id, name, substs },
            Ty::FnDef { def, name, sig, .. } => Ty::FnDef { def, name, sig, substs },
            _ => self,
        }
    }

    /// Replaces type parameters in this type using the given `Substs`. (So e.g.
    /// if `self` is `&[T]`, where type parameter T has index 0, and the
    /// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.)
    pub fn subst(self, substs: &Substs) -> Ty {
        self.fold(&mut |ty| match ty {
            Ty::Param { idx, name } => {
                if (idx as usize) < substs.0.len() {
                    substs.0[idx as usize].clone()
                } else {
                    // TODO: does this indicate a bug? i.e. should we always
                    // have substs for all type params? (they might contain the
                    // params themselves again...)
                    Ty::Param { idx, name }
                }
            }
            ty => ty,
        })
    }

    /// Returns the type parameters of this type if it has some (i.e. is an ADT
    /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
    fn substs(&self) -> Option<Substs> {
        match self {
            Ty::Adt { substs, .. } | Ty::FnDef { substs, .. } => Some(substs.clone()),
            _ => None,
        }
    }
}

impl fmt::Display for Ty {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Ty::Bool => write!(f, "bool"),
            Ty::Char => write!(f, "char"),
            Ty::Int(t) => write!(f, "{}", t.ty_to_string()),
            Ty::Float(t) => write!(f, "{}", t.ty_to_string()),
            Ty::Str => write!(f, "str"),
            Ty::Slice(t) | Ty::Array(t) => write!(f, "[{}]", t),
            Ty::RawPtr(t, m) => write!(f, "*{}{}", m.as_keyword_for_ptr(), t),
            Ty::Ref(t, m) => write!(f, "&{}{}", m.as_keyword_for_ref(), t),
            Ty::Never => write!(f, "!"),
            Ty::Tuple(ts) => {
                if ts.len() == 1 {
                    write!(f, "({},)", ts[0])
                } else {
                    join(ts.iter()).surround_with("(", ")").separator(", ").to_fmt(f)
                }
            }
            Ty::FnPtr(sig) => {
                join(sig.input.iter()).surround_with("fn(", ")").separator(", ").to_fmt(f)?;
                write!(f, " -> {}", sig.output)
            }
            Ty::FnDef { def, name, substs, sig, .. } => {
                match def {
                    CallableDef::Function(_) => write!(f, "fn {}", name)?,
                    CallableDef::Struct(_) | CallableDef::EnumVariant(_) => write!(f, "{}", name)?,
                }
                if substs.0.len() > 0 {
                    join(substs.0.iter()).surround_with("<", ">").separator(", ").to_fmt(f)?;
                }
                join(sig.input.iter()).surround_with("(", ")").separator(", ").to_fmt(f)?;
                write!(f, " -> {}", sig.output)
            }
            Ty::Adt { name, substs, .. } => {
                write!(f, "{}", name)?;
                if substs.0.len() > 0 {
                    join(substs.0.iter()).surround_with("<", ">").separator(", ").to_fmt(f)?;
                }
                Ok(())
            }
            Ty::Param { name, .. } => write!(f, "{}", name),
            Ty::Unknown => write!(f, "[unknown]"),
            Ty::Infer(..) => write!(f, "_"),
        }
    }
}
<commit_msg>A bit of cleanup in ty.rs<commit_after>//! The type system. We currently use this to infer types for completion, hover
//! information and various assists.

mod autoderef;
pub(crate) mod primitive;
#[cfg(test)]
mod tests;
pub(crate) mod method_resolution;
mod op;
mod lower;
mod infer;

use std::sync::Arc;
use std::{fmt, mem};

use join_to_string::join;

use crate::{Name, AdtDef, type_ref::Mutability};

pub(crate) use lower::{TypableDef, CallableDef, type_for_def, type_for_field};
pub(crate) use infer::{infer, InferenceResult, InferTy};

/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
///
/// This should be cheap to clone.
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Ty {
    /// The primitive boolean type. Written as `bool`.
    Bool,

    /// The primitive character type; holds a Unicode scalar value
    /// (a non-surrogate code point). Written as `char`.
    Char,

    /// A primitive integer type. For example, `i32`.
    Int(primitive::UncertainIntTy),

    /// A primitive floating-point type. For example, `f64`.
    Float(primitive::UncertainFloatTy),

    /// Structures, enumerations and unions.
    Adt {
        /// The definition of the struct/enum.
        def_id: AdtDef,
        /// The name, for displaying.
        name: Name,
        /// Substitutions for the generic parameters of the type.
        substs: Substs,
    },

    /// The pointee of a string slice. Written as `str`.
    Str,

    /// The pointee of an array slice.  Written as `[T]`.
    Slice(Arc<Ty>),

    /// An array with the given length. Written as `[T; n]`.
    Array(Arc<Ty>),

    /// A raw pointer. Written as `*mut T` or `*const T`
    RawPtr(Arc<Ty>, Mutability),

    /// A reference; a pointer with an associated lifetime. Written as
    /// `&'a mut T` or `&'a T`.
    Ref(Arc<Ty>, Mutability),

    /// The anonymous type of a function declaration/definition. Each
    /// function has a unique type, which is output (for a function
    /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
    ///
    /// This includes tuple struct / enum variant constructors as well.
    ///
    /// For example the type of `bar` here:
    ///
    /// ```rust
    /// fn foo() -> i32 { 1 }
    /// let bar = foo; // bar: fn() -> i32 {foo}
    /// ```
    FnDef {
        /// The definition of the function / constructor.
        def: CallableDef,
        /// For display
        name: Name,
        /// Parameters and return type
        sig: Arc<FnSig>,
        /// Substitutions for the generic parameters of the type
        substs: Substs,
    },

    /// A pointer to a function.  Written as `fn() -> i32`.
    ///
    /// For example the type of `bar` here:
    ///
    /// ```rust
    /// fn foo() -> i32 { 1 }
    /// let bar: fn() -> i32 = foo;
    /// ```
    FnPtr(Arc<FnSig>),

    /// The never type `!`.
    Never,

    /// A tuple type.  For example, `(i32, bool)`.
    Tuple(Arc<[Ty]>),

    /// A type parameter; for example, `T` in `fn f<T>(x: T) {}
    Param {
        /// The index of the parameter (starting with parameters from the
        /// surrounding impl, then the current function).
        idx: u32,
        /// The name of the parameter, for displaying.
        name: Name,
    },

    /// A type variable used during type checking. Not to be confused with a
    /// type parameter.
    Infer(InferTy),

    /// A placeholder for a type which could not be computed; this is propagated
    /// to avoid useless error messages. Doubles as a placeholder where type
    /// variables are inserted before type checking, since we want to try to
    /// infer a better type here anyway -- for the IDE use case, we want to try
    /// to infer as much as possible even in the presence of type errors.
    Unknown,
}

/// A list of substitutions for generic parameters.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Substs(Arc<[Ty]>);

impl Substs {
    pub fn empty() -> Substs {
        Substs(Arc::new([]))
    }
}

/// A function signature.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FnSig {
    input: Vec<Ty>,
    output: Ty,
}

impl Ty {
    pub fn unit() -> Self {
        Ty::Tuple(Arc::new([]))
    }

    pub fn walk(&self, f: &mut impl FnMut(&Ty)) {
        match self {
            Ty::Slice(t) | Ty::Array(t) => t.walk(f),
            Ty::RawPtr(t, _) => t.walk(f),
            Ty::Ref(t, _) => t.walk(f),
            Ty::Tuple(ts) => {
                for t in ts.iter() {
                    t.walk(f);
                }
            }
            Ty::FnPtr(sig) => {
                for input in &sig.input {
                    input.walk(f);
                }
                sig.output.walk(f);
            }
            Ty::FnDef { substs, sig, .. } => {
                for input in &sig.input {
                    input.walk(f);
                }
                sig.output.walk(f);
                for t in substs.0.iter() {
                    t.walk(f);
                }
            }
            Ty::Adt { substs, .. } => {
                for t in substs.0.iter() {
                    t.walk(f);
                }
            }
            Ty::Bool
            | Ty::Char
            | Ty::Int(_)
            | Ty::Float(_)
            | Ty::Str
            | Ty::Never
            | Ty::Param { .. }
            | Ty::Infer(_)
            | Ty::Unknown => {}
        }
        f(self);
    }

    fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
        match self {
            Ty::Slice(t) | Ty::Array(t) => Arc::make_mut(t).walk_mut(f),
            Ty::RawPtr(t, _) => Arc::make_mut(t).walk_mut(f),
            Ty::Ref(t, _) => Arc::make_mut(t).walk_mut(f),
            Ty::Tuple(ts) => {
                // Without an Arc::make_mut_slice, we can't avoid the clone here:
                let mut v: Vec<_> = ts.iter().cloned().collect();
                for t in &mut v {
                    t.walk_mut(f);
                }
                *ts = v.into();
            }
            Ty::FnPtr(sig) => {
                let sig_mut = Arc::make_mut(sig);
                for input in &mut sig_mut.input {
                    input.walk_mut(f);
                }
                sig_mut.output.walk_mut(f);
            }
            Ty::FnDef { substs, sig, .. } => {
                let sig_mut = Arc::make_mut(sig);
                for input in &mut sig_mut.input {
                    input.walk_mut(f);
                }
                sig_mut.output.walk_mut(f);
                // Without an Arc::make_mut_slice, we can't avoid the clone here:
                let mut v: Vec<_> = substs.0.iter().cloned().collect();
                for t in &mut v {
                    t.walk_mut(f);
                }
                substs.0 = v.into();
            }
            Ty::Adt { substs, .. } => {
                // Without an Arc::make_mut_slice, we can't avoid the clone here:
                let mut v: Vec<_> = substs.0.iter().cloned().collect();
                for t in &mut v {
                    t.walk_mut(f);
                }
                substs.0 = v.into();
            }
            Ty::Bool
            | Ty::Char
            | Ty::Int(_)
            | Ty::Float(_)
            | Ty::Str
            | Ty::Never
            | Ty::Param { .. }
            | Ty::Infer(_)
            | Ty::Unknown => {}
        }
        f(self);
    }

    fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Ty {
        self.walk_mut(&mut |ty_mut| {
            let ty = mem::replace(ty_mut, Ty::Unknown);
            *ty_mut = f(ty);
        });
        self
    }

    fn builtin_deref(&self) -> Option<Ty> {
        match self {
            Ty::Ref(t, _) => Some(Ty::clone(t)),
            Ty::RawPtr(t, _) => Some(Ty::clone(t)),
            _ => None,
        }
    }

    /// If this is a type with type parameters (an ADT or function), replaces
    /// the `Substs` for these type parameters with the given ones. (So e.g. if
    /// `self` is `Option<_>` and the substs contain `u32`, we'll have
    /// `Option<u32>` afterwards.)
    pub fn apply_substs(self, substs: Substs) -> Ty {
        match self {
            Ty::Adt { def_id, name, .. } => Ty::Adt { def_id, name, substs },
            Ty::FnDef { def, name, sig, .. } => Ty::FnDef { def, name, sig, substs },
            _ => self,
        }
    }

    /// Replaces type parameters in this type using the given `Substs`. (So e.g.
    /// if `self` is `&[T]`, where type parameter T has index 0, and the
    /// `Substs` contain `u32` at index 0, we'll have `&[u32]` afterwards.)
    pub fn subst(self, substs: &Substs) -> Ty {
        self.fold(&mut |ty| match ty {
            Ty::Param { idx, name } => {
                if (idx as usize) < substs.0.len() {
                    substs.0[idx as usize].clone()
                } else {
                    Ty::Param { idx, name }
                }
            }
            ty => ty,
        })
    }

    /// Returns the type parameters of this type if it has some (i.e. is an ADT
    /// or function); so if `self` is `Option<u32>`, this returns the `u32`.
    fn substs(&self) -> Option<Substs> {
        match self {
            Ty::Adt { substs, .. } | Ty::FnDef { substs, .. } => Some(substs.clone()),
            _ => None,
        }
    }
}

impl fmt::Display for Ty {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Ty::Bool => write!(f, "bool"),
            Ty::Char => write!(f, "char"),
            Ty::Int(t) => write!(f, "{}", t.ty_to_string()),
            Ty::Float(t) => write!(f, "{}", t.ty_to_string()),
            Ty::Str => write!(f, "str"),
            Ty::Slice(t) | Ty::Array(t) => write!(f, "[{}]", t),
            Ty::RawPtr(t, m) => write!(f, "*{}{}", m.as_keyword_for_ptr(), t),
            Ty::Ref(t, m) => write!(f, "&{}{}", m.as_keyword_for_ref(), t),
            Ty::Never => write!(f, "!"),
            Ty::Tuple(ts) => {
                if ts.len() == 1 {
                    write!(f, "({},)", ts[0])
                } else {
                    join(ts.iter()).surround_with("(", ")").separator(", ").to_fmt(f)
                }
            }
            Ty::FnPtr(sig) => {
                join(sig.input.iter()).surround_with("fn(", ")").separator(", ").to_fmt(f)?;
                write!(f, " -> {}", sig.output)
            }
            Ty::FnDef { def, name, substs, sig, .. } => {
                match def {
                    CallableDef::Function(_) => write!(f, "fn {}", name)?,
                    CallableDef::Struct(_) | CallableDef::EnumVariant(_) => write!(f, "{}", name)?,
                }
                if substs.0.len() > 0 {
                    join(substs.0.iter()).surround_with("<", ">").separator(", ").to_fmt(f)?;
                }
                join(sig.input.iter()).surround_with("(", ")").separator(", ").to_fmt(f)?;
                write!(f, " -> {}", sig.output)
            }
            Ty::Adt { name, substs, .. } => {
                write!(f, "{}", name)?;
                if substs.0.len() > 0 {
                    join(substs.0.iter()).surround_with("<", ">").separator(", ").to_fmt(f)?;
                }
                Ok(())
            }
            Ty::Param { name, .. } => write!(f, "{}", name),
            Ty::Unknown => write!(f, "[unknown]"),
            Ty::Infer(..) => write!(f, "_"),
        }
    }
}
<|endoftext|>


<commit_before>use std::{
    fs,
    collections::HashMap,
    path::{Path, PathBuf},
    process::{Command, Output, Stdio},
    io::{Error, ErrorKind}
};

use failure::bail;
use itertools::Itertools;

pub use teraron::{Mode, Overwrite, Verify};

pub type Result<T> = std::result::Result<T, failure::Error>;

pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron";
const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/ok";
const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/err";

pub const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs.tera";
pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera";
const TOOLCHAIN: &str = "stable";

#[derive(Debug)]
pub struct Test {
    pub name: String,
    pub text: String,
    pub ok: bool,
}

pub fn collect_tests(s: &str) -> Vec<(usize, Test)> {
    let mut res = vec![];
    let prefix = "// ";
    let comment_blocks = s
        .lines()
        .map(str::trim_start)
        .enumerate()
        .group_by(|(_idx, line)| line.starts_with(prefix));

    'outer: for (is_comment, block) in comment_blocks.into_iter() {
        if !is_comment {
            continue;
        }
        let mut block = block.map(|(idx, line)| (idx, &line[prefix.len()..]));

        let mut ok = true;
        let (start_line, name) = loop {
            match block.next() {
                Some((idx, line)) if line.starts_with("test ") => {
                    break (idx, line["test ".len()..].to_string());
                }
                Some((idx, line)) if line.starts_with("test_err ") => {
                    ok = false;
                    break (idx, line["test_err ".len()..].to_string());
                }
                Some(_) => (),
                None => continue 'outer,
            }
        };
        let text: String =
            itertools::join(block.map(|(_, line)| line).chain(::std::iter::once("")), "\n");
        assert!(!text.trim().is_empty() && text.ends_with('\n'));
        res.push((start_line, Test { name, text, ok }))
    }
    res
}

pub fn generate(mode: Mode) -> Result<()> {
    let grammar = project_root().join(GRAMMAR);
    let syntax_kinds = project_root().join(SYNTAX_KINDS);
    let ast = project_root().join(AST);
    teraron::generate(&syntax_kinds, &grammar, mode)?;
    teraron::generate(&ast, &grammar, mode)?;
    Ok(())
}

pub fn project_root() -> PathBuf {
    Path::new(&env!("CARGO_MANIFEST_DIR")).ancestors().nth(2).unwrap().to_path_buf()
}

pub fn run(cmdline: &str, dir: &str) -> Result<()> {
    do_run(cmdline, dir, |c| {
        c.stdout(Stdio::inherit());
    })
    .map(|_| ())
}

pub fn run_with_output(cmdline: &str, dir: &str) -> Result<Output> {
    do_run(cmdline, dir, |_| {})
}

pub fn run_rustfmt(mode: Mode) -> Result<()> {
    match Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => (),
        _ => install_rustfmt()?,
    };

    if mode == Verify {
        run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?;
    } else {
        run(&format!("rustup run {} -- cargo fmt", TOOLCHAIN), ".")?;
    }
    Ok(())
}

pub fn install_rustfmt() -> Result<()> {
    run(&format!("rustup install {}", TOOLCHAIN), ".")?;
    run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".")
}

pub fn install_format_hook() -> Result<()> {
    let result_path = Path::new("./.git/hooks/pre-commit");
    if !result_path.exists() {
        run("cargo build --package tools --bin pre-commit", ".")?;
        if cfg!(windows) {
            fs::copy("./target/debug/pre-commit.exe", result_path)?;
        } else {
            fs::copy("./target/debug/pre-commit", result_path)?;
        }
    } else {
        return Err(Error::new(ErrorKind::AlreadyExists, "Git hook already created").into());
    }
    Ok(())
}

pub fn run_fuzzer() -> Result<()> {
    match Command::new("cargo")
        .args(&["fuzz", "--help"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => (),
        _ => run("cargo install cargo-fuzz", ".")?,
    };

    run("rustup run nightly -- cargo fuzz run parser", "./crates/ra_syntax")
}

pub fn gen_tests(mode: Mode) -> Result<()> {
    let tests = tests_from_dir(&project_root().join(Path::new(GRAMMAR_DIR)))?;
    fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> {
        let tests_dir = project_root().join(into);
        if !tests_dir.is_dir() {
            fs::create_dir_all(&tests_dir)?;
        }
        // ok is never actually read, but it needs to be specified to create a Test in existing_tests
        let existing = existing_tests(&tests_dir, true)?;
        for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
            panic!("Test is deleted: {}", t);
        }

        let mut new_idx = existing.len() + 1;
        for (name, test) in tests {
            let path = match existing.get(name) {
                Some((path, _test)) => path.clone(),
                None => {
                    let file_name = format!("{:04}_{}.rs", new_idx, name);
                    new_idx += 1;
                    tests_dir.join(file_name)
                }
            };
            teraron::update(&path, &test.text, mode)?;
        }
        Ok(())
    }
    install_tests(&tests.ok, OK_INLINE_TESTS_DIR, mode)?;
    install_tests(&tests.err, ERR_INLINE_TESTS_DIR, mode)
}

fn do_run<F>(cmdline: &str, dir: &str, mut f: F) -> Result<Output>
where
    F: FnMut(&mut Command),
{
    eprintln!("\nwill run: {}", cmdline);
    let proj_dir = project_root().join(dir);
    let mut args = cmdline.split_whitespace();
    let exec = args.next().unwrap();
    let mut cmd = Command::new(exec);
    f(cmd.args(args).current_dir(proj_dir).stderr(Stdio::inherit()));
    let output = cmd.output()?;
    if !output.status.success() {
        bail!("`{}` exited with {}", cmdline, output.status);
    }
    Ok(output)
}

#[derive(Default, Debug)]
struct Tests {
    pub ok: HashMap<String, Test>,
    pub err: HashMap<String, Test>,
}

fn tests_from_dir(dir: &Path) -> Result<Tests> {
    let mut res = Tests::default();
    for entry in ::walkdir::WalkDir::new(dir) {
        let entry = entry.unwrap();
        if !entry.file_type().is_file() {
            continue;
        }
        if entry.path().extension().unwrap_or_default() != "rs" {
            continue;
        }
        process_file(&mut res, entry.path())?;
    }
    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
    process_file(&mut res, &grammar_rs)?;
    return Ok(res);
    fn process_file(res: &mut Tests, path: &Path) -> Result<()> {
        let text = fs::read_to_string(path)?;

        for (_, test) in collect_tests(&text) {
            if test.ok {
                if let Some(old_test) = res.ok.insert(test.name.clone(), test) {
                    bail!("Duplicate test: {}", old_test.name)
                }
            } else {
                if let Some(old_test) = res.err.insert(test.name.clone(), test) {
                    bail!("Duplicate test: {}", old_test.name)
                }
            }
        }
        Ok(())
    }
}

fn existing_tests(dir: &Path, ok: bool) -> Result<HashMap<String, (PathBuf, Test)>> {
    let mut res = HashMap::new();
    for file in fs::read_dir(dir)? {
        let file = file?;
        let path = file.path();
        if path.extension().unwrap_or_default() != "rs" {
            continue;
        }
        let name = {
            let file_name = path.file_name().unwrap().to_str().unwrap();
            file_name[5..file_name.len() - 3].to_string()
        };
        let text = fs::read_to_string(&path)?;
        let test = Test { name: name.clone(), text, ok };
        if let Some(old) = res.insert(name, (path, test)) {
            println!("Duplicate test: {:?}", old);
        }
    }
    Ok(res)
}
<commit_msg>Use EXE extension for pre-commit hook on Window<commit_after>use std::{
    fs,
    collections::HashMap,
    path::{Path, PathBuf},
    process::{Command, Output, Stdio},
    io::{Error, ErrorKind}
};

use failure::bail;
use itertools::Itertools;

pub use teraron::{Mode, Overwrite, Verify};

pub type Result<T> = std::result::Result<T, failure::Error>;

pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron";
const GRAMMAR_DIR: &str = "crates/ra_parser/src/grammar";
const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/ok";
const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/err";

pub const SYNTAX_KINDS: &str = "crates/ra_parser/src/syntax_kind/generated.rs.tera";
pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera";
const TOOLCHAIN: &str = "stable";

#[derive(Debug)]
pub struct Test {
    pub name: String,
    pub text: String,
    pub ok: bool,
}

pub fn collect_tests(s: &str) -> Vec<(usize, Test)> {
    let mut res = vec![];
    let prefix = "// ";
    let comment_blocks = s
        .lines()
        .map(str::trim_start)
        .enumerate()
        .group_by(|(_idx, line)| line.starts_with(prefix));

    'outer: for (is_comment, block) in comment_blocks.into_iter() {
        if !is_comment {
            continue;
        }
        let mut block = block.map(|(idx, line)| (idx, &line[prefix.len()..]));

        let mut ok = true;
        let (start_line, name) = loop {
            match block.next() {
                Some((idx, line)) if line.starts_with("test ") => {
                    break (idx, line["test ".len()..].to_string());
                }
                Some((idx, line)) if line.starts_with("test_err ") => {
                    ok = false;
                    break (idx, line["test_err ".len()..].to_string());
                }
                Some(_) => (),
                None => continue 'outer,
            }
        };
        let text: String =
            itertools::join(block.map(|(_, line)| line).chain(::std::iter::once("")), "\n");
        assert!(!text.trim().is_empty() && text.ends_with('\n'));
        res.push((start_line, Test { name, text, ok }))
    }
    res
}

pub fn generate(mode: Mode) -> Result<()> {
    let grammar = project_root().join(GRAMMAR);
    let syntax_kinds = project_root().join(SYNTAX_KINDS);
    let ast = project_root().join(AST);
    teraron::generate(&syntax_kinds, &grammar, mode)?;
    teraron::generate(&ast, &grammar, mode)?;
    Ok(())
}

pub fn project_root() -> PathBuf {
    Path::new(&env!("CARGO_MANIFEST_DIR")).ancestors().nth(2).unwrap().to_path_buf()
}

pub fn run(cmdline: &str, dir: &str) -> Result<()> {
    do_run(cmdline, dir, |c| {
        c.stdout(Stdio::inherit());
    })
    .map(|_| ())
}

pub fn run_with_output(cmdline: &str, dir: &str) -> Result<Output> {
    do_run(cmdline, dir, |_| {})
}

pub fn run_rustfmt(mode: Mode) -> Result<()> {
    match Command::new("rustup")
        .args(&["run", TOOLCHAIN, "--", "cargo", "fmt", "--version"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => (),
        _ => install_rustfmt()?,
    };

    if mode == Verify {
        run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?;
    } else {
        run(&format!("rustup run {} -- cargo fmt", TOOLCHAIN), ".")?;
    }
    Ok(())
}

pub fn install_rustfmt() -> Result<()> {
    run(&format!("rustup install {}", TOOLCHAIN), ".")?;
    run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".")
}

pub fn install_format_hook() -> Result<()> {
    let result_path = Path::new(if cfg!(windows) {
        "./.git/hooks/pre-commit.exe"
    } else {
        "./.git/hooks/pre-commit"
    });
    if !result_path.exists() {
        run("cargo build --package tools --bin pre-commit", ".")?;
        if cfg!(windows) {
            fs::copy("./target/debug/pre-commit.exe", result_path)?;
        } else {
            fs::copy("./target/debug/pre-commit", result_path)?;
        }
    } else {
        return Err(Error::new(ErrorKind::AlreadyExists, "Git hook already created").into());
    }
    Ok(())
}

pub fn run_fuzzer() -> Result<()> {
    match Command::new("cargo")
        .args(&["fuzz", "--help"])
        .stderr(Stdio::null())
        .stdout(Stdio::null())
        .status()
    {
        Ok(status) if status.success() => (),
        _ => run("cargo install cargo-fuzz", ".")?,
    };

    run("rustup run nightly -- cargo fuzz run parser", "./crates/ra_syntax")
}

pub fn gen_tests(mode: Mode) -> Result<()> {
    let tests = tests_from_dir(&project_root().join(Path::new(GRAMMAR_DIR)))?;
    fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> {
        let tests_dir = project_root().join(into);
        if !tests_dir.is_dir() {
            fs::create_dir_all(&tests_dir)?;
        }
        // ok is never actually read, but it needs to be specified to create a Test in existing_tests
        let existing = existing_tests(&tests_dir, true)?;
        for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
            panic!("Test is deleted: {}", t);
        }

        let mut new_idx = existing.len() + 1;
        for (name, test) in tests {
            let path = match existing.get(name) {
                Some((path, _test)) => path.clone(),
                None => {
                    let file_name = format!("{:04}_{}.rs", new_idx, name);
                    new_idx += 1;
                    tests_dir.join(file_name)
                }
            };
            teraron::update(&path, &test.text, mode)?;
        }
        Ok(())
    }
    install_tests(&tests.ok, OK_INLINE_TESTS_DIR, mode)?;
    install_tests(&tests.err, ERR_INLINE_TESTS_DIR, mode)
}

fn do_run<F>(cmdline: &str, dir: &str, mut f: F) -> Result<Output>
where
    F: FnMut(&mut Command),
{
    eprintln!("\nwill run: {}", cmdline);
    let proj_dir = project_root().join(dir);
    let mut args = cmdline.split_whitespace();
    let exec = args.next().unwrap();
    let mut cmd = Command::new(exec);
    f(cmd.args(args).current_dir(proj_dir).stderr(Stdio::inherit()));
    let output = cmd.output()?;
    if !output.status.success() {
        bail!("`{}` exited with {}", cmdline, output.status);
    }
    Ok(output)
}

#[derive(Default, Debug)]
struct Tests {
    pub ok: HashMap<String, Test>,
    pub err: HashMap<String, Test>,
}

fn tests_from_dir(dir: &Path) -> Result<Tests> {
    let mut res = Tests::default();
    for entry in ::walkdir::WalkDir::new(dir) {
        let entry = entry.unwrap();
        if !entry.file_type().is_file() {
            continue;
        }
        if entry.path().extension().unwrap_or_default() != "rs" {
            continue;
        }
        process_file(&mut res, entry.path())?;
    }
    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
    process_file(&mut res, &grammar_rs)?;
    return Ok(res);
    fn process_file(res: &mut Tests, path: &Path) -> Result<()> {
        let text = fs::read_to_string(path)?;

        for (_, test) in collect_tests(&text) {
            if test.ok {
                if let Some(old_test) = res.ok.insert(test.name.clone(), test) {
                    bail!("Duplicate test: {}", old_test.name)
                }
            } else {
                if let Some(old_test) = res.err.insert(test.name.clone(), test) {
                    bail!("Duplicate test: {}", old_test.name)
                }
            }
        }
        Ok(())
    }
}

fn existing_tests(dir: &Path, ok: bool) -> Result<HashMap<String, (PathBuf, Test)>> {
    let mut res = HashMap::new();
    for file in fs::read_dir(dir)? {
        let file = file?;
        let path = file.path();
        if path.extension().unwrap_or_default() != "rs" {
            continue;
        }
        let name = {
            let file_name = path.file_name().unwrap().to_str().unwrap();
            file_name[5..file_name.len() - 3].to_string()
        };
        let text = fs::read_to_string(&path)?;
        let test = Test { name: name.clone(), text, ok };
        if let Some(old) = res.insert(name, (path, test)) {
            println!("Duplicate test: {:?}", old);
        }
    }
    Ok(res)
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(non_upper_case_globals)]
//#![allow(unused_assignments)]
#![allow(unused_imports)]
//#![allow(unused_variables)]

use std::env;
use std::ffi::OsStr;
use std::fmt::Display;
use std::fs;
use std::fs::File;
use std::io::{ BufReader, BufWriter, };
use std::io::prelude::*;
use std::os::unix::net::UnixStream;
use std::os::unix::process::CommandExt;
use std::path::{ Path, PathBuf, };
use std::process::{ Command, exit, };

use jsonrpc_lite;
use jsonrpc_lite::JsonRpc;

use serde_json;
use serde_json::Value;

fn make_lsp_json_str(method: &str, params: &Value) -> Result<String, serde_json::error::Error> {
    let msg = json!({
        "jsonrpc": "2.0",
        "id": 1,
        "method": method,
        "params": params,
    });
    let request = serde_json::to_string(&msg)?;
    Ok(format!("Content-Length: {}\r\n\r\n{}", request.len(), request))
}

#[derive(Debug, PartialEq)]
/// A message header, as described in the Language Server Protocol specification.
enum LspHeader {
    ContentType,
    ContentLength(usize),
}

/// Given a reference to a reader, attempts to read a Language Server Protocol message,
/// blocking until a message is received.
fn read_message<B: BufRead>(reader: &mut B) -> Value {
    let mut buffer = String::new();
    let mut content_length: Option<usize> = None;

    // read in headers.
    loop {
            buffer.clear();
            reader.read_line(&mut buffer).unwrap();
            match &buffer {
                s if s.trim().is_empty() => { break }, // empty line is end of headers
                s => {
                    match parse_header(s) {
                        LspHeader::ContentLength(len) => content_length = Some(len),
                        LspHeader::ContentType => (), // utf-8 only currently allowed value
                    };
                }
            };
        }

    let content_length = content_length.ok_or_else(|| format!("missing content-length header: {}", buffer)).unwrap();
    // message body isn't newline terminated, so we read content_length bytes
    let mut body_buffer = vec![0; content_length];
    reader.read_exact(&mut body_buffer).unwrap();
    let body = String::from_utf8(body_buffer).unwrap();
    serde_json::from_str::<Value>(&body).unwrap()
}

const HEADER_CONTENT_LENGTH: &str = "content-length";
const HEADER_CONTENT_TYPE: &str = "content-type";

/// Given a header string, attempts to extract and validate the name and value parts.
fn parse_header(s: &str) -> LspHeader {
    let split: Vec<String> = s.split(": ").map(|s| s.trim().to_lowercase()).collect();
    if split.len() != 2 { panic!(format!("malformed header: {}", s)) }
    match split[0].as_ref() {
        HEADER_CONTENT_TYPE   => LspHeader::ContentType,
        HEADER_CONTENT_LENGTH => LspHeader::ContentLength(usize::from_str_radix(&split[1], 10).unwrap()),
        _ => panic!(format!("Unknown header: {}", s)),
    }
}

fn handle_msg_quietly<B: BufRead>(mut reader: B) {
    match serde_json::from_value(read_message(&mut reader)).unwrap() {
        JsonRpc::Request(obj)    => eprintln!("client received unexpected request: {:?}", obj),
        JsonRpc::Notification(_) => (),
        JsonRpc::Success(_)      => (),
        JsonRpc::Error(obj)      => println!("recv error: {:?}", obj),
    }
}

fn handle_msg<B: BufRead>(mut reader: B) {
    match serde_json::from_value(read_message(&mut reader)).unwrap() {
        JsonRpc::Request(obj)      => eprintln!("client received unexpected request: {:?}", obj),
        JsonRpc::Notification(obj) => println!("recv notification: {:?}", obj),
        JsonRpc::Success(obj)      => println!("recv success: {:?}", obj),
        JsonRpc::Error(obj)        => println!("recv error: {:?}", obj),
    }
}

enum ExitCode { Success, Failure }

fn handle_msg_to_exit_code<B: BufRead>(mut reader: B) -> ExitCode {
    let mut done = false;
    let mut success = false;
    let mut failure = false;

    loop {
        let json_rpc = serde_json::from_value(read_message(&mut reader)).unwrap();
        match json_rpc {
            JsonRpc::Request(ref obj)      => eprintln!("client received unexpected request: {:?}", obj),
            JsonRpc::Success(ref obj)      => println!("recv success: {:?}", obj),
            JsonRpc::Error(ref obj)        => println!("recv error: {:?}", obj),
            JsonRpc::Notification(ref obj) => {
                match json_rpc.get_method() {
                    Some("window/logMessage") => {
                        let params0 = json_rpc.get_params().unwrap();
                        let params = match params0 {
                            jsonrpc_lite::Params::Array(_) => panic!("not expecting array"),
                            jsonrpc_lite::Params::None(()) => panic!("not expecting none"),
                            jsonrpc_lite::Params::Map(kvs) => kvs,
                        };
                        let lvl = params["type"].as_i64().unwrap();
                        let msg = params["message"].as_str().unwrap();
                        println!("{}", msg);
                        if msg == "Exited with code 0" { success = true }
                        if msg == "Done" { done = true }
                        if lvl == 1 { failure = true }
                    },
                    Some("textDocument/publishDiagnostics") => {
                        let params0 = json_rpc.get_params().unwrap();
                        let params = match params0 {
                            jsonrpc_lite::Params::Array(_) => panic!("not expecting array"),
                            jsonrpc_lite::Params::None(()) => panic!("not expecting none"),
                            jsonrpc_lite::Params::Map(kvs) => kvs,
                        };
                        let uri = params["uri"].as_str().unwrap();
                        let diagnostics = params["diagnostics"].as_array().unwrap();
                        for diagnostic in diagnostics {
                            println!("{}: {}", uri, diagnostic);
                        }
                    },
                    Some(_) | _ => println!("recv notification: {:?}", obj),
                }
            },
        }

        // val Error = 1L
        // val Warning = 2L
        // val Info = 3L
        // val Log = 4L

        // 'runMain t.Main'     reports "[log] Exited with code 0" and then "[log] Done"
        // 'runMain t.BadMain'  reports "[log] Done"               and then "[error] Nonzero exit code: 1"
        // 'compile' w/ error   reports "[error] Compilation failed"
        // 'compile' w/o errors reports "[log] Done", but we can't distinguish that from BadMain's "[log] Done"...

        if success && done { return ExitCode::Success }
        if failure { return ExitCode::Failure }
    }
}

pub fn talk_to_client(port_file: File) {
    let json: serde_json::Value = serde_json::from_reader(port_file).unwrap();
    let uri = json["uri"].as_str().unwrap();
    // TODO: Use a less idiotic way to get the path from the URI
    let socket_file_path = &uri[8..];

    match UnixStream::connect(socket_file_path) {
        Ok(stream) => talk_to_client_impl(socket_file_path, stream),
        Err(_)     => {
            fs::remove_file("project/target/active.json").expect("Failed to delete port file");
            ::main()
        },
    }
}

fn talk_to_client_impl<P: AsRef<Path>>(socket_file_path: P, mut stream: UnixStream) {
    let json_str = make_lsp_json_str("initialize", &json!({})).unwrap();
    stream.write_all(json_str.as_bytes()).unwrap();
    stream.flush().unwrap();

    let mut reader = BufReader::new(stream);
    handle_msg_quietly(&mut reader);

    let mut args = env::args().skip(1); // skip the path of the executable
    let command_line = args.nth(0).expect("at least one argument to sbt when server already running");
    let json_str2 = make_lsp_json_str("sbt/exec", &json!({"commandLine": command_line})).unwrap();
    let mut stream2 = UnixStream::connect(socket_file_path).unwrap();
    stream2.write_all(json_str2.as_bytes()).unwrap();
    stream2.flush().unwrap();

    let reader2 = BufReader::new(stream2);
    match handle_msg_to_exit_code(reader2) {
        ExitCode::Failure => exit(1),
        ExitCode::Success => exit(0),
    }
}

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

    #[test]
    fn decode_ok() {
        let msg = r#"{"jsonrpc":"2.0","result":{"commandQueue":["collectAnalyses","shell"],"exitCode":0,"status":"Done"},"id":null}"#;
        let json = serde_json::from_str::<serde_json::Value>(&msg).unwrap();
        let json_rpc: JsonRpc = serde_json::from_value(json).expect("is JSON RPC");
    }

    #[test]
    #[should_panic(expected = "data did not match any variant of untagged enum JsonRpc")]
    fn decode_ko() {
        let msg = r#"{"jsonrpc":"2.0","result":{"commandQueue":["collectAnalyses","shell"],"exitCode":0,"status":"Done"}}"#;
        let json = serde_json::from_str::<serde_json::Value>(&msg).unwrap();
        let json_rpc: JsonRpc = serde_json::from_value(json).expect("is JSON RPC");
    }
}
<commit_msg>Specify the JSON RPC id<commit_after>#![allow(dead_code)]
#![allow(non_upper_case_globals)]
//#![allow(unused_assignments)]
#![allow(unused_imports)]
//#![allow(unused_variables)]

use std::env;
use std::ffi::OsStr;
use std::fmt::Display;
use std::fs;
use std::fs::File;
use std::io::{ BufReader, BufWriter, };
use std::io::prelude::*;
use std::os::unix::net::UnixStream;
use std::os::unix::process::CommandExt;
use std::path::{ Path, PathBuf, };
use std::process::{ Command, exit, };

use jsonrpc_lite;
use jsonrpc_lite::JsonRpc;

use serde_json;
use serde_json::Value;

type JsonRpcId = i64;

fn make_lsp_json_str(id: JsonRpcId, method: &str, params: &Value) -> Result<String, serde_json::error::Error> {
    let msg = json!({
        "jsonrpc": "2.0",
        "id": id,
        "method": method,
        "params": params,
    });
    let request = serde_json::to_string(&msg)?;
    Ok(format!("Content-Length: {}\r\n\r\n{}", request.len(), request))
}

#[derive(Debug, PartialEq)]
/// A message header, as described in the Language Server Protocol specification.
enum LspHeader {
    ContentType,
    ContentLength(usize),
}

/// Given a reference to a reader, attempts to read a Language Server Protocol message,
/// blocking until a message is received.
fn read_message<B: BufRead>(reader: &mut B) -> Value {
    let mut buffer = String::new();
    let mut content_length: Option<usize> = None;

    // read in headers.
    loop {
            buffer.clear();
            reader.read_line(&mut buffer).unwrap();
            match &buffer {
                s if s.trim().is_empty() => { break }, // empty line is end of headers
                s => {
                    match parse_header(s) {
                        LspHeader::ContentLength(len) => content_length = Some(len),
                        LspHeader::ContentType => (), // utf-8 only currently allowed value
                    };
                }
            };
        }

    let content_length = content_length.ok_or_else(|| format!("missing content-length header: {}", buffer)).unwrap();
    // message body isn't newline terminated, so we read content_length bytes
    let mut body_buffer = vec![0; content_length];
    reader.read_exact(&mut body_buffer).unwrap();
    let body = String::from_utf8(body_buffer).unwrap();
    serde_json::from_str::<Value>(&body).unwrap()
}

const HEADER_CONTENT_LENGTH: &str = "content-length";
const HEADER_CONTENT_TYPE: &str = "content-type";

/// Given a header string, attempts to extract and validate the name and value parts.
fn parse_header(s: &str) -> LspHeader {
    let split: Vec<String> = s.split(": ").map(|s| s.trim().to_lowercase()).collect();
    if split.len() != 2 { panic!(format!("malformed header: {}", s)) }
    match split[0].as_ref() {
        HEADER_CONTENT_TYPE   => LspHeader::ContentType,
        HEADER_CONTENT_LENGTH => LspHeader::ContentLength(usize::from_str_radix(&split[1], 10).unwrap()),
        _ => panic!(format!("Unknown header: {}", s)),
    }
}

fn handle_msg_quietly<B: BufRead>(mut reader: B) {
    match serde_json::from_value(read_message(&mut reader)).unwrap() {
        JsonRpc::Request(obj)    => eprintln!("client received unexpected request: {:?}", obj),
        JsonRpc::Notification(_) => (),
        JsonRpc::Success(_)      => (),
        JsonRpc::Error(obj)      => println!("recv error: {:?}", obj),
    }
}

fn handle_msg<B: BufRead>(mut reader: B) {
    match serde_json::from_value(read_message(&mut reader)).unwrap() {
        JsonRpc::Request(obj)      => eprintln!("client received unexpected request: {:?}", obj),
        JsonRpc::Notification(obj) => println!("recv notification: {:?}", obj),
        JsonRpc::Success(obj)      => println!("recv success: {:?}", obj),
        JsonRpc::Error(obj)        => println!("recv error: {:?}", obj),
    }
}

enum ExitCode { Success, Failure }

fn handle_msg_to_exit_code<B: BufRead>(mut reader: B) -> ExitCode {
    let mut done = false;
    let mut success = false;
    let mut failure = false;

    loop {
        let json_rpc = serde_json::from_value(read_message(&mut reader)).unwrap();
        match json_rpc {
            JsonRpc::Request(ref obj)      => eprintln!("client received unexpected request: {:?}", obj),
            JsonRpc::Success(ref obj)      => println!("recv success: {:?}", obj),
            JsonRpc::Error(ref obj)        => println!("recv error: {:?}", obj),
            JsonRpc::Notification(ref obj) => {
                match json_rpc.get_method() {
                    Some("window/logMessage") => {
                        let params0 = json_rpc.get_params().unwrap();
                        let params = match params0 {
                            jsonrpc_lite::Params::Array(_) => panic!("not expecting array"),
                            jsonrpc_lite::Params::None(()) => panic!("not expecting none"),
                            jsonrpc_lite::Params::Map(kvs) => kvs,
                        };
                        let lvl = params["type"].as_i64().unwrap();
                        let msg = params["message"].as_str().unwrap();
                        println!("{}", msg);
                        if msg == "Exited with code 0" { success = true }
                        if msg == "Done" { done = true }
                        if lvl == 1 { failure = true }
                    },
                    Some("textDocument/publishDiagnostics") => {
                        let params0 = json_rpc.get_params().unwrap();
                        let params = match params0 {
                            jsonrpc_lite::Params::Array(_) => panic!("not expecting array"),
                            jsonrpc_lite::Params::None(()) => panic!("not expecting none"),
                            jsonrpc_lite::Params::Map(kvs) => kvs,
                        };
                        let uri = params["uri"].as_str().unwrap();
                        let diagnostics = params["diagnostics"].as_array().unwrap();
                        for diagnostic in diagnostics {
                            println!("{}: {}", uri, diagnostic);
                        }
                    },
                    Some(_) | _ => println!("recv notification: {:?}", obj),
                }
            },
        }

        // val Error = 1L
        // val Warning = 2L
        // val Info = 3L
        // val Log = 4L

        // 'runMain t.Main'     reports "[log] Exited with code 0" and then "[log] Done"
        // 'runMain t.BadMain'  reports "[log] Done"               and then "[error] Nonzero exit code: 1"
        // 'compile' w/ error   reports "[error] Compilation failed"
        // 'compile' w/o errors reports "[log] Done", but we can't distinguish that from BadMain's "[log] Done"...

        if success && done { return ExitCode::Success }
        if failure { return ExitCode::Failure }
    }
}

pub fn talk_to_client(port_file: File) {
    let json: serde_json::Value = serde_json::from_reader(port_file).unwrap();
    let uri = json["uri"].as_str().unwrap();
    // TODO: Use a less idiotic way to get the path from the URI
    let socket_file_path = &uri[8..];

    match UnixStream::connect(socket_file_path) {
        Ok(stream) => talk_to_client_impl(socket_file_path, stream),
        Err(_)     => {
            fs::remove_file("project/target/active.json").expect("Failed to delete port file");
            ::main()
        },
    }
}

fn talk_to_client_impl<P: AsRef<Path>>(socket_file_path: P, mut stream: UnixStream) {
    let json_str = make_lsp_json_str(1, "initialize", &json!({})).unwrap();
    stream.write_all(json_str.as_bytes()).unwrap();
    stream.flush().unwrap();

    let mut reader = BufReader::new(stream);
    handle_msg_quietly(&mut reader);

    let mut args = env::args().skip(1); // skip the path of the executable
    let command_line = args.nth(0).expect("at least one argument to sbt when server already running");
    let json_str2 = make_lsp_json_str(2, "sbt/exec", &json!({"commandLine": command_line})).unwrap();
    let mut stream2 = UnixStream::connect(socket_file_path).unwrap();
    stream2.write_all(json_str2.as_bytes()).unwrap();
    stream2.flush().unwrap();

    let reader2 = BufReader::new(stream2);
    match handle_msg_to_exit_code(reader2) {
        ExitCode::Failure => exit(1),
        ExitCode::Success => exit(0),
    }
}

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

    #[test]
    fn decode_ok() {
        let msg = r#"{"jsonrpc":"2.0","result":{"commandQueue":["collectAnalyses","shell"],"exitCode":0,"status":"Done"},"id":null}"#;
        let json = serde_json::from_str::<serde_json::Value>(&msg).unwrap();
        let json_rpc: JsonRpc = serde_json::from_value(json).expect("is JSON RPC");
    }

    #[test]
    #[should_panic(expected = "data did not match any variant of untagged enum JsonRpc")]
    fn decode_ko() {
        let msg = r#"{"jsonrpc":"2.0","result":{"commandQueue":["collectAnalyses","shell"],"exitCode":0,"status":"Done"}}"#;
        let json = serde_json::from_str::<serde_json::Value>(&msg).unwrap();
        let json_rpc: JsonRpc = serde_json::from_value(json).expect("is JSON RPC");
    }
}
<|endoftext|>


<commit_before>//! Common client funcionalities.
extern crate rdkafka_sys as rdkafka;

use std::ffi::CString;
use std::os::raw::c_void;

use log::LogLevel;

use self::rdkafka::types::*;

use config::TopicConfig;
use error::{KafkaError, KafkaResult};
use util::{bytes_cstr_to_owned, cstr_to_owned};

/// A Context is an object that can store user-defined data and on which callbacks can be
/// defined. Refer to the list of methods to see which callbacks can currently be overridden.
/// The context must be thread safe, and might be owned by multiple threads.
pub trait Context: Send + Sync { }

/// An empty context that can be used when no context is needed.
#[derive(Clone)]
pub struct EmptyContext;

impl Context for EmptyContext { }

impl EmptyContext {
    pub fn new() -> EmptyContext {
        EmptyContext {}
    }
}

/// A native rdkafka-sys client.
pub struct NativeClient {
    ptr: *mut RDKafka,
}

// The library is completely thread safe, according to the documentation.
unsafe impl Sync for NativeClient {}
unsafe impl Send for NativeClient {}

impl NativeClient {
    /// Wraps a pointer to an RDKafka object and returns a new NativeClient.
    pub fn new(ptr: *mut RDKafka) -> NativeClient {
        NativeClient{ptr: ptr}
    }

    /// Returns the wrapped pointer to RDKafka.
    pub fn ptr(&self) -> *mut RDKafka {
        self.ptr
    }
}

/// A low level rdkafka client. This client shouldn't be used directly. The producer and consumer modules
/// provide different producer and consumer implementations based on top of `Client` that can be
/// used instead.
pub struct Client<C: Context> {
    native: NativeClient,
    context: Box<C>,
}

impl<C: Context> Client<C> {
    /// Creates a new Client given a configuration, a client type and a context.
    pub fn new(config_ptr: *mut RDKafkaConf, rd_kafka_type: RDKafkaType, context: C) -> KafkaResult<Client<C>> {
        let errstr = [0i8; 1024];
        let mut boxed_context = Box::new(context);
        unsafe { rdkafka::rd_kafka_conf_set_opaque(config_ptr, (&mut *boxed_context) as *mut C as *mut c_void) };

        let client_ptr =
            unsafe { rdkafka::rd_kafka_new(rd_kafka_type, config_ptr, errstr.as_ptr() as *mut i8, errstr.len()) };
        if client_ptr.is_null() {
            let descr = unsafe { bytes_cstr_to_owned(&errstr) };
            return Err(KafkaError::ClientCreation(descr));
        }

        // Set log level based on log crate's settings
        let log_level = if log_enabled!(LogLevel::Debug) {
            7
        } else if log_enabled!(LogLevel::Info) {
            5
        } else if log_enabled!(LogLevel::Warn) {
            4
        } else {
            3
        };
        unsafe { rdkafka::rd_kafka_set_log_level(client_ptr, log_level) };

        Ok(Client {
            native: NativeClient { ptr: client_ptr },
            context: boxed_context,
        })
    }

    /// Returns a pointer to the native rdkafka-sys client.
    pub fn native_ptr(&self) -> *mut RDKafka {
        self.native.ptr
    }

    /// Returns a reference to the context.
    pub fn context(&self) -> &C {
        self.context.as_ref()
    }
}

impl<C: Context> Drop for Client<C> {
    fn drop(&mut self) {
        trace!("Destroy rd_kafka");
        unsafe {
            rdkafka::rd_kafka_destroy(self.native.ptr);
            rdkafka::rd_kafka_wait_destroyed(1000);
        }
    }
}

/// Represents a Kafka topic with an associated producer.
pub struct Topic<'a, C: Context + 'a> {
    ptr: *mut RDKafkaTopic,
    _client: &'a Client<C>,
}

impl<'a, C: Context> Topic<'a, C> {
    /// Creates the Topic.
    pub fn new(client: &'a Client<C>, name: &str, topic_config: &TopicConfig) -> KafkaResult<Topic<'a, C>> {
        let name_ptr = CString::new(name.to_string()).unwrap();
        let config_ptr = try!(topic_config.create_native_config());
        let topic_ptr = unsafe { rdkafka::rd_kafka_topic_new(client.native.ptr, name_ptr.as_ptr(), config_ptr) };
        if topic_ptr.is_null() {
            Err(KafkaError::TopicCreation(name.to_string()))
        } else {
            let topic = Topic {
                ptr: topic_ptr,
                _client: client,
            };
            Ok(topic)
        }
    }

    /// Get topic's name
    pub fn get_name(&self) -> String {
        unsafe {
            cstr_to_owned(rdkafka::rd_kafka_topic_name(self.ptr))
        }
    }

    /// Returns a pointer to the correspondent rdkafka `RDKafkaTopic` stuct.
    pub fn ptr(&self) -> *mut RDKafkaTopic {
        self.ptr
    }
}

impl<'a, C: Context> Drop for Topic<'a, C> {
    fn drop(&mut self) {
        trace!("Destroy rd_kafka_topic");
        unsafe {
            rdkafka::rd_kafka_topic_destroy(self.ptr);
        }
    }
}

#[cfg(test)]
mod tests {
    // Just call everything to test there no panics by default, behavior
    // is tested in the integrations tests.

    use config::{ClientConfig,TopicConfig};
    use super::*;

    #[test]
    fn test_client() {
        let config_ptr = ClientConfig::new().create_native_config().unwrap();
        let client = Client::new(config_ptr, RDKafkaType::RD_KAFKA_PRODUCER, EmptyContext::new()).unwrap();
        assert!(!client.native_ptr().is_null());
    }

    #[test]
    fn test_topic() {
        let config_ptr = ClientConfig::new().create_native_config().unwrap();
        let client = Client::new(config_ptr, RDKafkaType::RD_KAFKA_CONSUMER, EmptyContext::new()).unwrap();
        let topic = Topic::new(&client, "topic_name", &TopicConfig::new()).unwrap();
        assert_eq!(topic.get_name(), "topic_name");
        assert!(!topic.ptr().is_null());
    }
}
<commit_msg>Fix build<commit_after>//! Common client funcionalities.
extern crate rdkafka_sys as rdkafka;

use std::ffi::CString;
use std::os::raw::c_void;

use log::LogLevel;

use self::rdkafka::types::*;

use config::TopicConfig;
use error::{KafkaError, KafkaResult};
use util::{bytes_cstr_to_owned, cstr_to_owned};

/// A Context is an object that can store user-defined data and on which callbacks can be
/// defined. Refer to the list of methods to see which callbacks can currently be overridden.
/// The context must be thread safe, and might be owned by multiple threads.
pub trait Context: Send + Sync { }

/// An empty context that can be used when no context is needed.
#[derive(Clone)]
pub struct EmptyContext;

impl Context for EmptyContext { }

impl EmptyContext {
    pub fn new() -> EmptyContext {
        EmptyContext {}
    }
}

/// A native rdkafka-sys client.
pub struct NativeClient {
    ptr: *mut RDKafka,
}

// The library is completely thread safe, according to the documentation.
unsafe impl Sync for NativeClient {}
unsafe impl Send for NativeClient {}

impl NativeClient {
    /// Wraps a pointer to an RDKafka object and returns a new NativeClient.
    pub fn new(ptr: *mut RDKafka) -> NativeClient {
        NativeClient{ptr: ptr}
    }

    /// Returns the wrapped pointer to RDKafka.
    pub fn ptr(&self) -> *mut RDKafka {
        self.ptr
    }
}

/// A low level rdkafka client. This client shouldn't be used directly. The producer and consumer modules
/// provide different producer and consumer implementations based on top of `Client` that can be
/// used instead.
pub struct Client<C: Context> {
    native: NativeClient,
    context: Box<C>,
}

impl<C: Context> Client<C> {
    /// Creates a new Client given a configuration, a client type and a context.
    pub fn new(config_ptr: *mut RDKafkaConf, rd_kafka_type: RDKafkaType, context: C) -> KafkaResult<Client<C>> {
        let errstr = [0i8; 1024];
        let mut boxed_context = Box::new(context);
        unsafe { rdkafka::rd_kafka_conf_set_opaque(config_ptr, (&mut *boxed_context) as *mut C as *mut c_void) };

        let client_ptr =
            unsafe { rdkafka::rd_kafka_new(rd_kafka_type, config_ptr, errstr.as_ptr() as *mut i8, errstr.len()) };
        if client_ptr.is_null() {
            let descr = unsafe { bytes_cstr_to_owned(&errstr) };
            return Err(KafkaError::ClientCreation(descr));
        }

        // Set log level based on log crate's settings
        let log_level = if log_enabled!(LogLevel::Debug) {
            7
        } else if log_enabled!(LogLevel::Info) {
            5
        } else if log_enabled!(LogLevel::Warn) {
            4
        } else {
            3
        };
        unsafe { rdkafka::rd_kafka_set_log_level(client_ptr, log_level) };

        Ok(Client {
            native: NativeClient { ptr: client_ptr },
            context: boxed_context,
        })
    }

    /// Returns a pointer to the native rdkafka-sys client.
    pub fn native_ptr(&self) -> *mut RDKafka {
        self.native.ptr
    }

    /// Returns a reference to the context.
    pub fn context(&self) -> &C {
        self.context.as_ref()
    }
}

impl<C: Context> Drop for Client<C> {
    fn drop(&mut self) {
        trace!("Destroy rd_kafka");
        unsafe {
            rdkafka::rd_kafka_destroy(self.native.ptr);
            rdkafka::rd_kafka_wait_destroyed(1000);
        }
    }
}

/// Represents a Kafka topic with an associated producer.
pub struct Topic<'a, C: Context + 'a> {
    ptr: *mut RDKafkaTopic,
    _client: &'a Client<C>,
}

impl<'a, C: Context> Topic<'a, C> {
    /// Creates the Topic.
    pub fn new(client: &'a Client<C>, name: &str, topic_config: &TopicConfig) -> KafkaResult<Topic<'a, C>> {
        let name_ptr = CString::new(name.to_string()).unwrap();
        let config_ptr = try!(topic_config.create_native_config());
        let topic_ptr = unsafe { rdkafka::rd_kafka_topic_new(client.native.ptr, name_ptr.as_ptr(), config_ptr) };
        if topic_ptr.is_null() {
            Err(KafkaError::TopicCreation(name.to_string()))
        } else {
            let topic = Topic {
                ptr: topic_ptr,
                _client: client,
            };
            Ok(topic)
        }
    }

    /// Get topic's name
    pub fn get_name(&self) -> String {
        unsafe {
            cstr_to_owned(rdkafka::rd_kafka_topic_name(self.ptr))
        }
    }

    /// Returns a pointer to the correspondent rdkafka `RDKafkaTopic` stuct.
    pub fn ptr(&self) -> *mut RDKafkaTopic {
        self.ptr
    }
}

impl<'a, C: Context> Drop for Topic<'a, C> {
    fn drop(&mut self) {
        trace!("Destroy rd_kafka_topic");
        unsafe {
            rdkafka::rd_kafka_topic_destroy(self.ptr);
        }
    }
}

#[cfg(test)]
mod tests {
    // Just call everything to test there no panics by default, behavior
    // is tested in the integrations tests.

    extern crate rdkafka_sys as rdkafka;
    use config::{ClientConfig,TopicConfig};
    use self::rdkafka::types::*;
    use super::*;

    #[test]
    fn test_client() {
        let config_ptr = ClientConfig::new().create_native_config().unwrap();
        let client = Client::new(config_ptr, RDKafkaType::RD_KAFKA_PRODUCER, EmptyContext::new()).unwrap();
        assert!(!client.native_ptr().is_null());
    }

    #[test]
    fn test_topic() {
        let config_ptr = ClientConfig::new().create_native_config().unwrap();
        let client = Client::new(config_ptr, RDKafkaType::RD_KAFKA_CONSUMER, EmptyContext::new()).unwrap();
        let topic = Topic::new(&client, "topic_name", &TopicConfig::new()).unwrap();
        assert_eq!(topic.get_name(), "topic_name");
        assert!(!topic.ptr().is_null());
    }
}
<|endoftext|>


<commit_before>//! Authentication related structure and functions
use std::env;

use crate::{conv, enums::*, functions::*, types::*};

/// Main struct to authenticate a user
///
/// You need to create an instance of it to start an authentication process. If you
/// want a simple password-based authentication, you can use `Client::with_password`,
/// and to the following flow:
///
/// ```no_run
/// use pam::Client;
///
/// let mut client = Client::with_password("system-auth")
///         .expect("Failed to init PAM client.");
/// // Preset the login & password we will use for authentication
/// client.conversation_mut().set_credentials("login", "password");
/// // Actually try to authenticate:
/// client.authenticate().expect("Authentication failed!");
/// // Now that we are authenticated, it's possible to open a sesssion:
/// client.open_session().expect("Failed to open a session!");
/// ```
///
/// If you wish to customise the PAM conversation function, you should rather create your
/// client with `Client::with_handler`, providing a struct implementing the
/// `conv::Conversation` trait. You can then mutably access your conversation handler using the
/// `Client::handler_mut` method.
///
/// By default, the `Client` will close any opened session when dropped. If you don't
/// want this, you can change its `close_on_drop` field to `False`.
pub struct Client<'a, C: conv::Conversation> {
    /// Flag indicating whether the Client should close the session on drop
    pub close_on_drop: bool,
    handle: &'a mut PamHandle,
    conversation: Box<C>,
    is_authenticated: bool,
    has_open_session: bool,
    last_code: PamReturnCode,
}

impl<'a> Client<'a, conv::PasswordConv> {
    /// Create a new `Client` with the given service name and a password-based conversation
    pub fn with_password(service: &str) -> PamResult<Client<'a, conv::PasswordConv>> {
        Client::with_conversation(service, conv::PasswordConv::new())
    }
}

impl<'a, C: conv::Conversation> Client<'a, C> {
    /// Create a new `Client` with the given service name and conversation handler
    pub fn with_conversation(service: &str, conversation: C) -> PamResult<Client<'a, C>> {
        let mut conversation = Box::new(conversation);
        let conv = conv::into_pam_conv(&mut *conversation);

        let handle = start(service, None, &conv)?;
        Ok(Client {
            close_on_drop: true,
            handle,
            conversation,
            is_authenticated: false,
            has_open_session: false,
            last_code: PamReturnCode::Success,
        })
    }

    /// Immutable access to the conversation handler of this Client
    pub fn conversation(&self) -> &C {
        &*self.conversation
    }

    /// Mutable access to the conversation handler of this Client
    pub fn conversation_mut(&mut self) -> &mut C {
        &mut *self.conversation
    }

    /// Perform the authentication with the provided credentials
    pub fn authenticate(&mut self) -> PamResult<()> {
        self.last_code = authenticate(self.handle, PamFlag::None);
        if self.last_code != PamReturnCode::Success {
            // No need to reset here
            return Err(From::from(self.last_code));
        }

        self.is_authenticated = true;

        self.last_code = acct_mgmt(self.handle, PamFlag::None);
        if self.last_code != PamReturnCode::Success {
            // Probably not strictly neccessary but better be sure
            return self.reset();
        }
        Ok(())
    }

    /// Open a session for a previously authenticated user and
    /// initialize the environment appropriately (in PAM and regular enviroment variables).
    pub fn open_session(&mut self) -> PamResult<()> {
        if !self.is_authenticated {
            //TODO: is this the right return code?
            return Err(PamReturnCode::Perm_Denied.into());
        }

        self.last_code = setcred(self.handle, PamFlag::Establish_Cred);
        if self.last_code != PamReturnCode::Success {
            return self.reset();
        }

        self.last_code = open_session(self.handle, false);
        if self.last_code != PamReturnCode::Success {
            return self.reset();
        }

        // Follow openSSH and call pam_setcred before and after open_session
        self.last_code = setcred(self.handle, PamFlag::Reinitialize_Cred);
        if self.last_code != PamReturnCode::Success {
            return self.reset();
        }

        self.has_open_session = true;
        self.initialize_environment()
    }

    // Initialize the client environment with common variables.
    // Currently always called from Client.open_session()
    fn initialize_environment(&mut self) -> PamResult<()> {
        use users::os::unix::UserExt;

        let user = users::get_user_by_name(self.conversation.username()).unwrap_or_else(|| {
            panic!(
                "Could not get user by name: {:?}",
                self.conversation.username()
            )
        });

        // Set some common environment variables
        self.set_env(
            "USER",
            user.name()
                .to_str()
                .expect("Unix usernames should be valid UTF-8"),
        )?;
        self.set_env(
            "LOGNAME",
            user.name()
                .to_str()
                .expect("Unix usernames should be valid UTF-8"),
        )?;
        self.set_env("HOME", user.home_dir().to_str().unwrap())?;
        self.set_env("PWD", user.home_dir().to_str().unwrap())?;
        self.set_env("SHELL", user.shell().to_str().unwrap())?;
        // Note: We don't set PATH here, as this should be the job of `pam_env.so`

        Ok(())
    }

    // Utility function to set an environment variable in PAM and the process
    fn set_env(&mut self, key: &str, value: &str) -> PamResult<()> {
        // Set regular environment variable
        env::set_var(key, value);

        // Set pam environment variable
        if getenv(self.handle, key).is_ok() {
            let name_value = format!("{}={}", key, value);
            putenv(self.handle, &name_value)
        } else {
            Ok(())
        }
    }

    // Utility function to reset the pam handle in case of intermediate errors
    fn reset(&mut self) -> PamResult<()> {
        setcred(self.handle, PamFlag::Delete_Cred);
        self.is_authenticated = false;
        Err(From::from(self.last_code))
    }
}

impl<'a, C: conv::Conversation> Drop for Client<'a, C> {
    fn drop(&mut self) {
        if self.has_open_session && self.close_on_drop {
            close_session(self.handle, false);
        }
        let code = setcred(self.handle, PamFlag::Delete_Cred);
        end(self.handle, code);
    }
}
<commit_msg>Add client chauthtok (#28)<commit_after>//! Authentication related structure and functions
use std::env;

use crate::{conv, enums::*, functions::*, types::*};

/// Main struct to authenticate a user
///
/// You need to create an instance of it to start an authentication process. If you
/// want a simple password-based authentication, you can use `Client::with_password`,
/// and to the following flow:
///
/// ```no_run
/// use pam::Client;
///
/// let mut client = Client::with_password("system-auth")
///         .expect("Failed to init PAM client.");
/// // Preset the login & password we will use for authentication
/// client.conversation_mut().set_credentials("login", "password");
/// // Actually try to authenticate:
/// client.authenticate().expect("Authentication failed!");
/// // Now that we are authenticated, it's possible to open a sesssion:
/// client.open_session().expect("Failed to open a session!");
/// ```
///
/// If you wish to customise the PAM conversation function, you should rather create your
/// client with `Client::with_handler`, providing a struct implementing the
/// `conv::Conversation` trait. You can then mutably access your conversation handler using the
/// `Client::handler_mut` method.
///
/// By default, the `Client` will close any opened session when dropped. If you don't
/// want this, you can change its `close_on_drop` field to `False`.
pub struct Client<'a, C: conv::Conversation> {
    /// Flag indicating whether the Client should close the session on drop
    pub close_on_drop: bool,
    handle: &'a mut PamHandle,
    conversation: Box<C>,
    is_authenticated: bool,
    has_open_session: bool,
    last_code: PamReturnCode,
}

impl<'a> Client<'a, conv::PasswordConv> {
    /// Create a new `Client` with the given service name and a password-based conversation
    pub fn with_password(service: &str) -> PamResult<Client<'a, conv::PasswordConv>> {
        Client::with_conversation(service, conv::PasswordConv::new())
    }
}

impl<'a, C: conv::Conversation> Client<'a, C> {
    /// Create a new `Client` with the given service name and conversation handler
    pub fn with_conversation(service: &str, conversation: C) -> PamResult<Client<'a, C>> {
        let mut conversation = Box::new(conversation);
        let conv = conv::into_pam_conv(&mut *conversation);

        let handle = start(service, None, &conv)?;
        Ok(Client {
            close_on_drop: true,
            handle,
            conversation,
            is_authenticated: false,
            has_open_session: false,
            last_code: PamReturnCode::Success,
        })
    }

    /// Immutable access to the conversation handler of this Client
    pub fn conversation(&self) -> &C {
        &*self.conversation
    }

    /// Mutable access to the conversation handler of this Client
    pub fn conversation_mut(&mut self) -> &mut C {
        &mut *self.conversation
    }

    /// Perform the authentication with the provided credentials
    pub fn authenticate(&mut self) -> PamResult<()> {
        self.last_code = authenticate(self.handle, PamFlag::None);
        if self.last_code != PamReturnCode::Success {
            // No need to reset here
            return Err(From::from(self.last_code));
        }

        self.is_authenticated = true;

        self.last_code = acct_mgmt(self.handle, PamFlag::None);
        if self.last_code != PamReturnCode::Success {
            // Probably not strictly neccessary but better be sure
            return self.reset();
        }
        Ok(())
    }

    /// Perform the chauthtok to support password update
    pub fn change_authentication_token(&mut self, flags: PamFlag) -> PamResult<()> {
        self.last_code = chauthtok(self.handle, flags);
        if self.last_code != PamReturnCode::Success {
            // No need to reset here
            return Err(From::from(self.last_code));
        }
        Ok(())
    }

    /// Open a session for a previously authenticated user and
    /// initialize the environment appropriately (in PAM and regular enviroment variables).
    pub fn open_session(&mut self) -> PamResult<()> {
        if !self.is_authenticated {
            //TODO: is this the right return code?
            return Err(PamReturnCode::Perm_Denied.into());
        }

        self.last_code = setcred(self.handle, PamFlag::Establish_Cred);
        if self.last_code != PamReturnCode::Success {
            return self.reset();
        }

        self.last_code = open_session(self.handle, false);
        if self.last_code != PamReturnCode::Success {
            return self.reset();
        }

        // Follow openSSH and call pam_setcred before and after open_session
        self.last_code = setcred(self.handle, PamFlag::Reinitialize_Cred);
        if self.last_code != PamReturnCode::Success {
            return self.reset();
        }

        self.has_open_session = true;
        self.initialize_environment()
    }

    // Initialize the client environment with common variables.
    // Currently always called from Client.open_session()
    fn initialize_environment(&mut self) -> PamResult<()> {
        use users::os::unix::UserExt;

        let user = users::get_user_by_name(self.conversation.username()).unwrap_or_else(|| {
            panic!(
                "Could not get user by name: {:?}",
                self.conversation.username()
            )
        });

        // Set some common environment variables
        self.set_env(
            "USER",
            user.name()
                .to_str()
                .expect("Unix usernames should be valid UTF-8"),
        )?;
        self.set_env(
            "LOGNAME",
            user.name()
                .to_str()
                .expect("Unix usernames should be valid UTF-8"),
        )?;
        self.set_env("HOME", user.home_dir().to_str().unwrap())?;
        self.set_env("PWD", user.home_dir().to_str().unwrap())?;
        self.set_env("SHELL", user.shell().to_str().unwrap())?;
        // Note: We don't set PATH here, as this should be the job of `pam_env.so`

        Ok(())
    }

    // Utility function to set an environment variable in PAM and the process
    fn set_env(&mut self, key: &str, value: &str) -> PamResult<()> {
        // Set regular environment variable
        env::set_var(key, value);

        // Set pam environment variable
        if getenv(self.handle, key).is_ok() {
            let name_value = format!("{}={}", key, value);
            putenv(self.handle, &name_value)
        } else {
            Ok(())
        }
    }

    // Utility function to reset the pam handle in case of intermediate errors
    fn reset(&mut self) -> PamResult<()> {
        setcred(self.handle, PamFlag::Delete_Cred);
        self.is_authenticated = false;
        Err(From::from(self.last_code))
    }
}

impl<'a, C: conv::Conversation> Drop for Client<'a, C> {
    fn drop(&mut self) {
        if self.has_open_session && self.close_on_drop {
            close_session(self.handle, false);
        }
        let code = setcred(self.handle, PamFlag::Delete_Cred);
        end(self.handle, code);
    }
}
<|endoftext|>


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

//! Traits for conversions between types.
//!
//! The traits in this module provide a general way to talk about
//! conversions from one type to another. They follow the standard
//! Rust conventions of `as`/`to`/`into`/`from`.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A cheap, reference-to-reference conversion.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsRef<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_ref(&self) -> &T;
}

/// A cheap, mutable reference-to-mutable reference conversion.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsMut<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_mut(&mut self) -> &mut T;
}

/// A conversion that consumes `self`, which may or may not be
/// expensive.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Into<T>: Sized {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn into(self) -> T;
}

/// Construct `Self` via a conversion.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait From<T> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn from(T) -> Self;
}

////////////////////////////////////////////////////////////////////////////////
// GENERIC IMPLS
////////////////////////////////////////////////////////////////////////////////

// As lifts over &
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// As lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a mut T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// FIXME (#23442): replace the above impls for &/&mut with the following more general one:
// // As lifts over Deref
// impl<D: ?Sized + Deref, U: ?Sized> AsRef<U> for D where D::Target: AsRef<U> {
//     fn as_ref(&self) -> &U {
//         self.deref().as_ref()
//     }
// }

// AsMut lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsMut<U> for &'a mut T where T: AsMut<U> {
    fn as_mut(&mut self) -> &mut U {
        (*self).as_mut()
    }
}

// FIXME (#23442): replace the above impl for &mut with the following more general one:
// // AsMut lifts over DerefMut
// impl<D: ?Sized + Deref, U: ?Sized> AsMut<U> for D where D::Target: AsMut<U> {
//     fn as_mut(&mut self) -> &mut U {
//         self.deref_mut().as_mut()
//     }
// }

// From implies Into
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, U> Into<U> for T where U: From<T> {
    fn into(self) -> U {
        U::from(self)
    }
}

// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> From<T> for T {
    fn from(t: T) -> T { t }
}

////////////////////////////////////////////////////////////////////////////////
// CONCRETE IMPLS
////////////////////////////////////////////////////////////////////////////////

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsRef<[T]> for [T] {
    fn as_ref(&self) -> &[T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsMut<[T]> for [T] {
    fn as_mut(&mut self) -> &mut [T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<str> for str {
    fn as_ref(&self) -> &str {
        self
    }
}
<commit_msg>Add examples for Convert<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Traits for conversions between types.
//!
//! The traits in this module provide a general way to talk about conversions from one type to
//! another. They follow the standard Rust conventions of `as`/`to`/`into`/`from`.
//!
//! Like many traits, these are often used as bounds for generic functions, to support arguments of
//! multiple types.
//!
//! See each trait for usage examples.

#![stable(feature = "rust1", since = "1.0.0")]

use marker::Sized;

/// A cheap, reference-to-reference conversion.
///
/// # Examples
///
/// Both `String` and `&str` implement `AsRef<str>`:
///
/// ```
/// fn is_hello<T: AsRef<str>>(s: T) {
///    assert_eq!("hello", s.as_ref());
/// }
///
/// let s = "hello";
/// is_hello(s);
///
/// let s = "hello".to_string();
/// is_hello(s);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsRef<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_ref(&self) -> &T;
}

/// A cheap, mutable reference-to-mutable reference conversion.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait AsMut<T: ?Sized> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn as_mut(&mut self) -> &mut T;
}

/// A conversion that consumes `self`, which may or may not be expensive.
///
/// # Examples
///
/// `String` implements `Into<Vec<u8>>`:
///
/// ```
/// fn is_hello<T: Into<Vec<u8>>>(s: T) {
///    let bytes = b"hello".to_vec();
///    assert_eq!(bytes, s.into());
/// }
///
/// let s = "hello".to_string();
/// is_hello(s);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait Into<T>: Sized {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn into(self) -> T;
}

/// Construct `Self` via a conversion.
///
/// # Examples
///
/// `String` implements `From<&str>`:
///
/// ```
/// let s = "hello";
/// let string = "hello".to_string();
///
/// let other_string: String = From::from(s);
///
/// assert_eq!(string, other_string);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub trait From<T> {
    /// Performs the conversion.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn from(T) -> Self;
}

////////////////////////////////////////////////////////////////////////////////
// GENERIC IMPLS
////////////////////////////////////////////////////////////////////////////////

// As lifts over &
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// As lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for &'a mut T where T: AsRef<U> {
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

// FIXME (#23442): replace the above impls for &/&mut with the following more general one:
// // As lifts over Deref
// impl<D: ?Sized + Deref, U: ?Sized> AsRef<U> for D where D::Target: AsRef<U> {
//     fn as_ref(&self) -> &U {
//         self.deref().as_ref()
//     }
// }

// AsMut lifts over &mut
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T: ?Sized, U: ?Sized> AsMut<U> for &'a mut T where T: AsMut<U> {
    fn as_mut(&mut self) -> &mut U {
        (*self).as_mut()
    }
}

// FIXME (#23442): replace the above impl for &mut with the following more general one:
// // AsMut lifts over DerefMut
// impl<D: ?Sized + Deref, U: ?Sized> AsMut<U> for D where D::Target: AsMut<U> {
//     fn as_mut(&mut self) -> &mut U {
//         self.deref_mut().as_mut()
//     }
// }

// From implies Into
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, U> Into<U> for T where U: From<T> {
    fn into(self) -> U {
        U::from(self)
    }
}

// From (and thus Into) is reflexive
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> From<T> for T {
    fn from(t: T) -> T { t }
}

////////////////////////////////////////////////////////////////////////////////
// CONCRETE IMPLS
////////////////////////////////////////////////////////////////////////////////

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsRef<[T]> for [T] {
    fn as_ref(&self) -> &[T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsMut<[T]> for [T] {
    fn as_mut(&mut self) -> &mut [T] {
        self
    }
}

#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<str> for str {
    fn as_ref(&self) -> &str {
        self
    }
}
<|endoftext|>