<commit_before>use value::{InternedString, Pair, Value};
use value::Value::*;
use cell_gc::{GcHeapSession, GcLeaf};
use cell_gc::collections::VecRef;

#[derive(IntoHeap)]
pub enum Expr<'h> {
    /// A constant (`quote` expressions produce this, but also numbers and
    /// other self-evaluating values).
    Con(Value<'h>),

    /// A variable-expression (evaluates to the variable's value).
    Var(GcLeaf<InternedString>),

    /// A lambda expression.
    Fun(CodeRef<'h>),

    /// A function call expression (an application).
    App(VecRef<'h, Expr<'h>>),

    /// A sequence-expression (`begin` used as an expression).
    Seq(VecRef<'h, Expr<'h>>),

    /// A conditional expression (`if`).
    If(IfRef<'h>),

    /// A definition.
    Def(DefRef<'h>),

    /// An assignment expression (`set!`).
    Set(DefRef<'h>),

    /// A `letrec*` expression.
    Letrec(LetrecRef<'h>),

    /// Evaluates to the current global environment.
    /// Used to implement (interactive-environment), for (eval).
    ToplevelEnv,
}

#[derive(IntoHeap)]
pub struct Code<'h> {
    pub params: VecRef<'h, GcLeaf<InternedString>>,
    pub rest: bool,
    pub body: Expr<'h>,
}

#[derive(IntoHeap)]
pub struct Def<'h> {
    pub name: GcLeaf<InternedString>,
    pub value: Expr<'h>
}

#[derive(IntoHeap)]
pub struct If<'h> {
    pub cond: Expr<'h>,
    pub t_expr: Expr<'h>,
    pub f_expr: Expr<'h>,
}

#[derive(IntoHeap)]
pub struct Letrec<'h> {
    pub names: VecRef<'h, GcLeaf<InternedString>>,
    pub exprs: VecRef<'h, Expr<'h>>,
    pub body: Expr<'h>
}

// Convert the linked list of a `<body>` to a vector; also splice in the
// contents of `(begin)` expressions nested within the `<body>`.
fn flatten_body<'h>(forms: Value<'h>, out: &mut Vec<Value<'h>>) -> Result<(), String> {
    for form_res in forms {
        let form = form_res?;
        if let Cons(ref pair) = form {
            if let Symbol(op) = pair.car() {
                if op.as_str() == "begin" {
                    flatten_body(pair.cdr(), out)?;
                    continue;
                }
            }
        }
        out.push(form);
    }
    Ok(())
}

fn is_definition<'h>(form: &Value<'h>) -> bool {
    if let Cons(ref pair) = *form {
        if let Symbol(op) = pair.car() {
            if op.as_str() == "define" {
                return true;
            }
        }
    }
    false
}

// Compile the body of a lambda or letrec*.
fn compile_body<'h>(
    hs: &mut GcHeapSession<'h>,
    body_list: Value<'h>,
) -> Result<Expr<'h>, String> {
    let mut forms = vec![];
    flatten_body(body_list, &mut forms)?;

    let mut names = vec![];
    let mut exprs = vec![];

    let mut i = 0;
    while i < forms.len() && is_definition(&forms[i]) {
        let (name, expr) = parse_define(hs, forms[i].clone())?;
        names.push(name);
        exprs.push(expr);
        i += 1;
    }

    if i == forms.len() {
        return Err("expression required".into());
    }

    let names = hs.alloc(names);
    let exprs = hs.alloc(exprs);
    let body_exprs: Result<Vec<Expr>, String> = forms
        .drain(i..)
        .map(|form| compile_expr(hs, form))
        .collect();
    let body = seq(hs, body_exprs?);
    Ok(Expr::Letrec(hs.alloc(Letrec {
        names,
        exprs,
        body,
    })))
}

fn seq<'h>(hs: &mut GcHeapSession<'h>, mut exprs: Vec<Expr<'h>>) -> Expr<'h> {
    if exprs.len() == 0 {
        Expr::Con(Value::Unspecified)
    } else if exprs.len() == 1 {
        exprs.pop().unwrap()
    } else {
        Expr::Seq(hs.alloc(exprs))
    }
}

/// On success, returns the two parts of a `(define)` that we care about: the
/// name to define and the compiled expression to populate it.
fn parse_define<'h>(hs: &mut GcHeapSession<'h>, mut defn: Value<'h>)
    -> Result<(GcLeaf<InternedString>, Expr<'h>), String>
{
    loop {
        let (define_symbol, tail) = defn.as_pair("internal error")?;
        let (pattern, rest) = tail.as_pair("(define) with no name")?;
        match pattern {
            Symbol(ident) => {
                let (expr, rest) = rest.as_pair("(define) with no value")?;
                match rest {
                    Nil => {}
                    _ => {
                        return Err(
                            "too many arguments in (define)".to_string(),
                        )
                    }
                };

                let value = compile_expr(hs, expr)?;
                return Ok((ident, value));
            }
            Cons(pair) => {
                // Build desugared definition and compile that.
                let name = pair.car();
                let formals = pair.cdr();

                // Transform `(define (,name ,@formals) ,@rest)
                // to        `(define ,name (lambda ,formals ,@rest))
                let lambda_cdr = Cons(hs.alloc(Pair {
                    car: formals,
                    cdr: rest,
                }));
                let lambda = Cons(hs.alloc(Pair {
                    car: Symbol(GcLeaf::new(InternedString::get("lambda"))),
                    cdr: lambda_cdr,
                }));
                let defn_cddr = Cons(hs.alloc(Pair {
                    car: lambda,
                    cdr: Nil,
                }));
                let defn_cdr = Cons(hs.alloc(Pair {
                    car: name,
                    cdr: defn_cddr,
                }));
                defn = Cons(hs.alloc(Pair {
                    car: define_symbol,
                    cdr: defn_cdr
                }));
            }
            _ => return Err("(define) with a non-symbol name".to_string())
        }
    }
}

pub fn compile_toplevel<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Value<'h>,
) -> Result<Expr<'h>, String> {
    // TODO: support (begin) here
    if is_definition(&expr) {
        let (name, value) = parse_define(hs, expr)?;
        Ok(Expr::Def(hs.alloc(Def { name, value })))
    } else {
        compile_expr(hs, expr)
    }
}

pub fn compile_expr<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Value<'h>,
) -> Result<Expr<'h>, String> {
    match expr {
        Symbol(s) => Ok(Expr::Var(s)),

        Cons(p) => {
            let f = p.car();
            if let Symbol(ref s) = f {
                if s.as_str() == "lambda" {
                    let (mut param_list, body_forms) = p.cdr().as_pair("syntax error in lambda")?;

                    let mut names = vec![];
                    while let Cons(pair) = param_list {
                        if let Symbol(s) = pair.car() {
                            names.push(s);
                        } else {
                            return Err("syntax error in lambda arguments".to_string());
                        }
                        param_list = pair.cdr();
                    }
                    let rest =
                        match param_list {
                            Nil => false,
                            Symbol(rest_name) => {
                                names.push(rest_name);
                                true
                            }
                            _ => return Err("syntax error in lambda arguments".to_string())
                        };

                    let params = hs.alloc(names);
                    let body = compile_body(hs, body_forms)?;
                    return Ok(Expr::Fun(hs.alloc(Code { params, rest, body })));
                } else if s.as_str() == "quote" {
                    let (datum, rest) = p.cdr().as_pair("(quote) with no arguments")?;
                    if !rest.is_nil() {
                        return Err("too many arguments to (quote)".to_string());
                    }
                    return Ok(Expr::Con(datum));
                } else if s.as_str() == "if" {
                    let (cond, rest) = p.cdr().as_pair("(if) with no arguments")?;
                    let cond = compile_expr(hs, cond)?;
                    let (tc, rest) = rest.as_pair("missing arguments after (if COND)")?;
                    let t_expr = compile_expr(hs, tc)?;
                    let f_expr =
                        if rest == Nil {
                            Expr::Con(Unspecified)
                        } else {
                            let (fc, rest) =
                                rest.as_pair("missing 'else' argument after (if COND X)")?;
                            if !rest.is_nil() {
                                return Err("too many arguments in (if) expression".to_string());
                            }
                            compile_expr(hs, fc)?
                        };
                    return Ok(Expr::If(hs.alloc(If { cond, t_expr, f_expr })));
                } else if s.as_str() == "begin" {
                    // In expression context, this is sequencing, not splicing.
                    let mut exprs = vec![];
                    for expr_result in p.cdr() {
                        let expr = expr_result?;
                        exprs.push(compile_expr(hs, expr)?);
                    }
                    return Ok(Expr::Seq(hs.alloc(exprs)));
                } else if s.as_str() == "define" {
                    // In expression context, definitions aren't allowed.
                    return Err("(define) is allowed only at toplevel or in the body \
                                of a function or let-form".into());
                } else if s.as_str() == "letrec" || s.as_str() == "letrec*" {
                    // Treat (letrec) forms just like (letrec*). Nonstandard in
                    // R6RS, which requires implementations to detect invalid
                    // references to letrec bindings before they're bound. But
                    // R5RS does not require this, and anyway well-behaved
                    // programs won't care.
                    let (bindings, body_forms) = p.cdr().as_pair("letrec*: bindings required")?;
                    let mut names = vec![];
                    let mut exprs = vec![];
                    for binding_result in bindings {
                        let binding = binding_result?;
                        let (name_v, rest) = binding.as_pair("letrec*: invalid binding")?;
                        let name = name_v.as_symbol("letrec*: name required")?;
                        let (expr, rest) = rest.as_pair("letrec*: value required for binding")?;
                        if !rest.is_nil() {
                            return Err("(letrec*): too many arguments".to_string());
                        }
                        names.push(GcLeaf::new(name));
                        exprs.push(compile_expr(hs, expr)?);
                    }
                    let names = hs.alloc(names);
                    let exprs = hs.alloc(exprs);
                    let body = compile_body(hs, body_forms)?;
                    return Ok(Expr::Letrec(hs.alloc(Letrec {
                        names,
                        exprs,
                        body,
                    })));
                } else if s.as_str() == "set!" {
                    let (first, rest) = p.cdr().as_pair("(set!) with no name")?;
                    let name = first.as_symbol("(set!) first argument must be a name")?;
                    let (expr, rest) = rest.as_pair("(set!) with no value")?;
                    if !rest.is_nil() {
                        return Err("(set!): too many arguments".to_string());
                    }
                    let value = compile_expr(hs, expr)?;
                    return Ok(Expr::Set(hs.alloc(Def {
                        name: GcLeaf::new(name),
                        value: value
                    })));
                }
            }

            let subexprs: Vec<Expr<'h>> =
                Cons(p)
                    .into_iter()
                    .map(|v| compile_expr(hs, v?))
                    .collect::<Result<_, String>>()?;
            Ok(Expr::App(hs.alloc(subexprs)))
        }

        // Self-evaluating values.
        Bool(v) => Ok(Expr::Con(Bool(v))),
        Int(v) => Ok(Expr::Con(Int(v))),
        Char(v) => Ok(Expr::Con(Char(v))),
        ImmString(v) => Ok(Expr::Con(ImmString(v))),

        // Note: Not sure what R6RS says about "three-dimensional" code,
        // eval code containing "constants" (either quoted or self-evaluating)
        // that are passed through. Possibly both this and the (quote) case
        // should do more work, to "flatten" the constants.
        StringObj(v) => Ok(Expr::Con(StringObj(v))),

        // Everything else is an error.
        _ => Err(format!("not an expression")),
    }
}
<commit_msg>lisp: impl Debug for Expr, the type of compiled code.<commit_after>use value::{InternedString, Pair, Value};
use value::Value::*;
use cell_gc::{GcHeapSession, GcLeaf};
use cell_gc::collections::VecRef;
use std::fmt;

#[derive(IntoHeap)]
pub enum Expr<'h> {
    /// A constant (`quote` expressions produce this, but also numbers and
    /// other self-evaluating values).
    Con(Value<'h>),

    /// A variable-expression (evaluates to the variable's value).
    Var(GcLeaf<InternedString>),

    /// A lambda expression.
    Fun(CodeRef<'h>),

    /// A function call expression (an application).
    App(VecRef<'h, Expr<'h>>),

    /// A sequence-expression (`begin` used as an expression).
    Seq(VecRef<'h, Expr<'h>>),

    /// A conditional expression (`if`).
    If(IfRef<'h>),

    /// A definition.
    Def(DefRef<'h>),

    /// An assignment expression (`set!`).
    Set(DefRef<'h>),

    /// A `letrec*` expression.
    Letrec(LetrecRef<'h>),

    /// Evaluates to the current global environment.
    /// Used to implement (interactive-environment), for (eval).
    ToplevelEnv,
}

impl<'h> fmt::Debug for Expr<'h> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Expr::Con(ref v) => write!(f, "'{}", v),
            Expr::Var(ref s) => write!(f, "{}", Value::Symbol(s.clone())),
            Expr::Fun(ref c) => write!(f, "(lambda ... {:?})", c.body()),
            Expr::App(ref args) =>
                write!(f, "({})",
                       (0..args.len())
                       .map(|i| format!("{:?}", args.get(i)))
                       .collect::<Vec<String>>()
                       .join(" ")),
            Expr::Seq(ref exprs) =>
                write!(f, "(begin {})",
                       (0..exprs.len())
                       .map(|i| format!("{:?}", exprs.get(i)))
                       .collect::<Vec<String>>()
                       .join(" ")),
            Expr::If(ref r) =>
                write!(f, "(if {:?} {:?} {:?})", r.cond(), r.t_expr(), r.f_expr()),
            Expr::Def(ref r) =>
                write!(f, "(define {} {:?})", Value::Symbol(r.name()), r.value()),
            Expr::Set(ref r) =>
                write!(f, "(set! {} {:?})", Value::Symbol(r.name()), r.value()),
            Expr::Letrec(ref r) =>
                write!(f, "(letrec ({}) {:?})",
                       (0..r.names().len())
                       .map(|i| format!("({} {:?})",
                                        Value::Symbol(r.names().get(i)),
                                        r.exprs().get(i)))
                       .collect::<Vec<String>>()
                       .join(" "),
                       r.body()),
            Expr::ToplevelEnv => write!(f, "#<toplevel-env>"),
        }
    }
}

#[derive(IntoHeap)]
pub struct Code<'h> {
    pub params: VecRef<'h, GcLeaf<InternedString>>,
    pub rest: bool,
    pub body: Expr<'h>,
}

#[derive(IntoHeap)]
pub struct Def<'h> {
    pub name: GcLeaf<InternedString>,
    pub value: Expr<'h>
}

#[derive(IntoHeap)]
pub struct If<'h> {
    pub cond: Expr<'h>,
    pub t_expr: Expr<'h>,
    pub f_expr: Expr<'h>,
}

#[derive(IntoHeap)]
pub struct Letrec<'h> {
    pub names: VecRef<'h, GcLeaf<InternedString>>,
    pub exprs: VecRef<'h, Expr<'h>>,
    pub body: Expr<'h>
}

// Convert the linked list of a `<body>` to a vector; also splice in the
// contents of `(begin)` expressions nested within the `<body>`.
fn flatten_body<'h>(forms: Value<'h>, out: &mut Vec<Value<'h>>) -> Result<(), String> {
    for form_res in forms {
        let form = form_res?;
        if let Cons(ref pair) = form {
            if let Symbol(op) = pair.car() {
                if op.as_str() == "begin" {
                    flatten_body(pair.cdr(), out)?;
                    continue;
                }
            }
        }
        out.push(form);
    }
    Ok(())
}

fn is_definition<'h>(form: &Value<'h>) -> bool {
    if let Cons(ref pair) = *form {
        if let Symbol(op) = pair.car() {
            if op.as_str() == "define" {
                return true;
            }
        }
    }
    false
}

// Compile the body of a lambda or letrec*.
fn compile_body<'h>(
    hs: &mut GcHeapSession<'h>,
    body_list: Value<'h>,
) -> Result<Expr<'h>, String> {
    let mut forms = vec![];
    flatten_body(body_list, &mut forms)?;

    let mut names = vec![];
    let mut exprs = vec![];

    let mut i = 0;
    while i < forms.len() && is_definition(&forms[i]) {
        let (name, expr) = parse_define(hs, forms[i].clone())?;
        names.push(name);
        exprs.push(expr);
        i += 1;
    }

    if i == forms.len() {
        return Err("expression required".into());
    }

    let names = hs.alloc(names);
    let exprs = hs.alloc(exprs);
    let body_exprs: Result<Vec<Expr>, String> = forms
        .drain(i..)
        .map(|form| compile_expr(hs, form))
        .collect();
    let body = seq(hs, body_exprs?);
    Ok(Expr::Letrec(hs.alloc(Letrec {
        names,
        exprs,
        body,
    })))
}

fn seq<'h>(hs: &mut GcHeapSession<'h>, mut exprs: Vec<Expr<'h>>) -> Expr<'h> {
    if exprs.len() == 0 {
        Expr::Con(Value::Unspecified)
    } else if exprs.len() == 1 {
        exprs.pop().unwrap()
    } else {
        Expr::Seq(hs.alloc(exprs))
    }
}

/// On success, returns the two parts of a `(define)` that we care about: the
/// name to define and the compiled expression to populate it.
fn parse_define<'h>(hs: &mut GcHeapSession<'h>, mut defn: Value<'h>)
    -> Result<(GcLeaf<InternedString>, Expr<'h>), String>
{
    loop {
        let (define_symbol, tail) = defn.as_pair("internal error")?;
        let (pattern, rest) = tail.as_pair("(define) with no name")?;
        match pattern {
            Symbol(ident) => {
                let (expr, rest) = rest.as_pair("(define) with no value")?;
                match rest {
                    Nil => {}
                    _ => {
                        return Err(
                            "too many arguments in (define)".to_string(),
                        )
                    }
                };

                let value = compile_expr(hs, expr)?;
                return Ok((ident, value));
            }
            Cons(pair) => {
                // Build desugared definition and compile that.
                let name = pair.car();
                let formals = pair.cdr();

                // Transform `(define (,name ,@formals) ,@rest)
                // to        `(define ,name (lambda ,formals ,@rest))
                let lambda_cdr = Cons(hs.alloc(Pair {
                    car: formals,
                    cdr: rest,
                }));
                let lambda = Cons(hs.alloc(Pair {
                    car: Symbol(GcLeaf::new(InternedString::get("lambda"))),
                    cdr: lambda_cdr,
                }));
                let defn_cddr = Cons(hs.alloc(Pair {
                    car: lambda,
                    cdr: Nil,
                }));
                let defn_cdr = Cons(hs.alloc(Pair {
                    car: name,
                    cdr: defn_cddr,
                }));
                defn = Cons(hs.alloc(Pair {
                    car: define_symbol,
                    cdr: defn_cdr
                }));
            }
            _ => return Err("(define) with a non-symbol name".to_string())
        }
    }
}

pub fn compile_toplevel<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Value<'h>,
) -> Result<Expr<'h>, String> {
    // TODO: support (begin) here
    if is_definition(&expr) {
        let (name, value) = parse_define(hs, expr)?;
        Ok(Expr::Def(hs.alloc(Def { name, value })))
    } else {
        compile_expr(hs, expr)
    }
}

pub fn compile_expr<'h>(
    hs: &mut GcHeapSession<'h>,
    expr: Value<'h>,
) -> Result<Expr<'h>, String> {
    match expr {
        Symbol(s) => Ok(Expr::Var(s)),

        Cons(p) => {
            let f = p.car();
            if let Symbol(ref s) = f {
                if s.as_str() == "lambda" {
                    let (mut param_list, body_forms) = p.cdr().as_pair("syntax error in lambda")?;

                    let mut names = vec![];
                    while let Cons(pair) = param_list {
                        if let Symbol(s) = pair.car() {
                            names.push(s);
                        } else {
                            return Err("syntax error in lambda arguments".to_string());
                        }
                        param_list = pair.cdr();
                    }
                    let rest =
                        match param_list {
                            Nil => false,
                            Symbol(rest_name) => {
                                names.push(rest_name);
                                true
                            }
                            _ => return Err("syntax error in lambda arguments".to_string())
                        };

                    let params = hs.alloc(names);
                    let body = compile_body(hs, body_forms)?;
                    return Ok(Expr::Fun(hs.alloc(Code { params, rest, body })));
                } else if s.as_str() == "quote" {
                    let (datum, rest) = p.cdr().as_pair("(quote) with no arguments")?;
                    if !rest.is_nil() {
                        return Err("too many arguments to (quote)".to_string());
                    }
                    return Ok(Expr::Con(datum));
                } else if s.as_str() == "if" {
                    let (cond, rest) = p.cdr().as_pair("(if) with no arguments")?;
                    let cond = compile_expr(hs, cond)?;
                    let (tc, rest) = rest.as_pair("missing arguments after (if COND)")?;
                    let t_expr = compile_expr(hs, tc)?;
                    let f_expr =
                        if rest == Nil {
                            Expr::Con(Unspecified)
                        } else {
                            let (fc, rest) =
                                rest.as_pair("missing 'else' argument after (if COND X)")?;
                            if !rest.is_nil() {
                                return Err("too many arguments in (if) expression".to_string());
                            }
                            compile_expr(hs, fc)?
                        };
                    return Ok(Expr::If(hs.alloc(If { cond, t_expr, f_expr })));
                } else if s.as_str() == "begin" {
                    // In expression context, this is sequencing, not splicing.
                    let mut exprs = vec![];
                    for expr_result in p.cdr() {
                        let expr = expr_result?;
                        exprs.push(compile_expr(hs, expr)?);
                    }
                    return Ok(Expr::Seq(hs.alloc(exprs)));
                } else if s.as_str() == "define" {
                    // In expression context, definitions aren't allowed.
                    return Err("(define) is allowed only at toplevel or in the body \
                                of a function or let-form".into());
                } else if s.as_str() == "letrec" || s.as_str() == "letrec*" {
                    // Treat (letrec) forms just like (letrec*). Nonstandard in
                    // R6RS, which requires implementations to detect invalid
                    // references to letrec bindings before they're bound. But
                    // R5RS does not require this, and anyway well-behaved
                    // programs won't care.
                    let (bindings, body_forms) = p.cdr().as_pair("letrec*: bindings required")?;
                    let mut names = vec![];
                    let mut exprs = vec![];
                    for binding_result in bindings {
                        let binding = binding_result?;
                        let (name_v, rest) = binding.as_pair("letrec*: invalid binding")?;
                        let name = name_v.as_symbol("letrec*: name required")?;
                        let (expr, rest) = rest.as_pair("letrec*: value required for binding")?;
                        if !rest.is_nil() {
                            return Err("(letrec*): too many arguments".to_string());
                        }
                        names.push(GcLeaf::new(name));
                        exprs.push(compile_expr(hs, expr)?);
                    }
                    let names = hs.alloc(names);
                    let exprs = hs.alloc(exprs);
                    let body = compile_body(hs, body_forms)?;
                    return Ok(Expr::Letrec(hs.alloc(Letrec {
                        names,
                        exprs,
                        body,
                    })));
                } else if s.as_str() == "set!" {
                    let (first, rest) = p.cdr().as_pair("(set!) with no name")?;
                    let name = first.as_symbol("(set!) first argument must be a name")?;
                    let (expr, rest) = rest.as_pair("(set!) with no value")?;
                    if !rest.is_nil() {
                        return Err("(set!): too many arguments".to_string());
                    }
                    let value = compile_expr(hs, expr)?;
                    return Ok(Expr::Set(hs.alloc(Def {
                        name: GcLeaf::new(name),
                        value: value
                    })));
                }
            }

            let subexprs: Vec<Expr<'h>> =
                Cons(p)
                    .into_iter()
                    .map(|v| compile_expr(hs, v?))
                    .collect::<Result<_, String>>()?;
            Ok(Expr::App(hs.alloc(subexprs)))
        }

        // Self-evaluating values.
        Bool(v) => Ok(Expr::Con(Bool(v))),
        Int(v) => Ok(Expr::Con(Int(v))),
        Char(v) => Ok(Expr::Con(Char(v))),
        ImmString(v) => Ok(Expr::Con(ImmString(v))),

        // Note: Not sure what R6RS says about "three-dimensional" code,
        // eval code containing "constants" (either quoted or self-evaluating)
        // that are passed through. Possibly both this and the (quote) case
        // should do more work, to "flatten" the constants.
        StringObj(v) => Ok(Expr::Con(StringObj(v))),

        // Everything else is an error.
        _ => Err(format!("not an expression")),
    }
}
<|endoftext|>


<commit_before>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use anyhow::Result;
use std::process::{Command, Output};
use structopt::StructOpt;

const MIN_RUST_VERTION: &str = "1.47.0";
const NODE_VERSION: &str = "10.22.1";
const NVM_VERSION: &str = "0.35.3";

#[derive(StructOpt)]
struct DuniterXTask {
    #[structopt(long)]
    skip_npm: bool,
    #[structopt(subcommand)]
    command: DuniterXTaskCommand,
}

#[derive(StructOpt)]
enum DuniterXTaskCommand {
    Build {
        #[structopt(long)]
        production: bool,
    },
    Test,
}

fn main() -> Result<()> {
    let args = DuniterXTask::from_args();

    if !version_check::is_min_version(MIN_RUST_VERTION).unwrap_or(false)
        && exec_should_success(Command::new("rustup").args(&["update", "stable"])).is_err()
    {
        eprintln!(
                "Duniter requires Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.",
                MIN_RUST_VERTION
            );
        std::process::exit(1);
    }
    Command::new("rustc").arg("--version").status()?;
    Command::new("cargo").arg("--version").status()?;

    if !args.skip_npm {
        println!("Check node version …");
        if exec_and_get_stdout(Command::new("node").arg("-v"))
            .unwrap_or_default()
            .trim_end()
            != format!("v{}", NODE_VERSION)
        {
            println!("Install node v{} …", NODE_VERSION);
            install_and_use_node_version()?;
        } else {
            println!("Node v{} already installed ✔", NODE_VERSION);
        }
    }
    match args.command {
        DuniterXTaskCommand::Build { production } => build(args.skip_npm, production),
        DuniterXTaskCommand::Test => test(args.skip_npm),
    }
}

fn install_and_use_node_version() -> Result<()> {
    if exec_should_success(Command::new("nvm").arg("--version")).is_err() {
        println!("Install nvm v{} …", NVM_VERSION);
        let nvm_install_script = exec_and_get_stdout(Command::new("wget").args(&[
            "-qO-",
            &format!(
                "https://raw.githubusercontent.com/nvm-sh/nvm/v{}/install.sh",
                NVM_VERSION
            ),
        ]))?;
        run_script::spawn(
            &nvm_install_script,
            &vec![],
            &run_script::ScriptOptions::new(),
        )?
        .wait_with_output()?;
        //exec_should_success(Command::new("bash").arg(nvm_install_script))?;
    }
    exec_should_success(Command::new("nvm").args(&["install", NODE_VERSION]))?;
    exec_should_success(Command::new("nvm").args(&["use", NODE_VERSION]))
}

fn exec_and_get_stdout(command: &mut Command) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = command.output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn exec_should_success(command: &mut Command) -> Result<()> {
    if !command.status()?.success() {
        std::process::exit(1);
    } else {
        Ok(())
    }
}

fn build(skip_npm: bool, production: bool) -> Result<()> {
    if !skip_npm {
        exec_should_success(Command::new("npm").args(&["add", "duniter-ui"]))?;
        exec_should_success(
            Command::new("npm")
                .env("NEON_BUILD_RELEASE", "true")
                .arg("install"),
        )?;
        if production {
            exec_should_success(Command::new("npm").args(&["prune", "--production"]))?;
        }
    }
    println!("Build duniter-launcher …");
    exec_should_success(Command::new("cargo").args(&["build", "--release"]))?;
    std::fs::copy("target/release/duniter", "bin/duniter")?;
    Ok(())
}

fn test(skip_npm: bool) -> Result<()> {
    exec_should_success(Command::new("cargo").args(&["test", "--all"]))?;
    if !skip_npm {
        exec_should_success(Command::new("npm").arg("test"))?;
    }
    Ok(())
}
<commit_msg>[build] ask user to install nvm itself<commit_after>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use anyhow::Result;
use std::process::{Command, Output};
use structopt::StructOpt;

const MIN_RUST_VERTION: &str = "1.47.0";
const NODE_VERSION: &str = "10.22.1";
const NVM_VERSION: &str = "0.35.3";

#[derive(StructOpt)]
struct DuniterXTask {
    #[structopt(long)]
    skip_npm: bool,
    #[structopt(subcommand)]
    command: DuniterXTaskCommand,
}

#[derive(StructOpt)]
enum DuniterXTaskCommand {
    Build {
        #[structopt(long)]
        production: bool,
    },
    Test,
}

fn main() -> Result<()> {
    let args = DuniterXTask::from_args();

    if !version_check::is_min_version(MIN_RUST_VERTION).unwrap_or(false)
        && exec_should_success(Command::new("rustup").args(&["update", "stable"])).is_err()
    {
        eprintln!(
                "Duniter requires Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.",
                MIN_RUST_VERTION
            );
        std::process::exit(1);
    }
    Command::new("rustc").arg("--version").status()?;
    Command::new("cargo").arg("--version").status()?;

    if !args.skip_npm {
        println!("Check node version …");
        if exec_and_get_stdout(Command::new("node").arg("-v"))
            .unwrap_or_default()
            .trim_end()
            != format!("v{}", NODE_VERSION)
        {
            println!("Install node v{} …", NODE_VERSION);
            install_and_use_node_version()?;
        } else {
            println!("Node v{} already installed ✔", NODE_VERSION);
        }
    }
    match args.command {
        DuniterXTaskCommand::Build { production } => build(args.skip_npm, production),
        DuniterXTaskCommand::Test => test(args.skip_npm),
    }
}

fn install_and_use_node_version() -> Result<()> {
    if exec_should_success(Command::new("nvm").arg("--version")).is_err() {
        println!("Duniter need nvm to build, please install it: https://raw.githubusercontent.com/nvm-sh/nvm/v{}/install.sh", NVM_VERSION);
        std::process::abort();
    }
    exec_should_success(Command::new("nvm").args(&["install", NODE_VERSION]))?;
    exec_should_success(Command::new("nvm").args(&["use", NODE_VERSION]))
}

fn exec_and_get_stdout(command: &mut Command) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = command.output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn exec_should_success(command: &mut Command) -> Result<()> {
    if !command.status()?.success() {
        std::process::exit(1);
    } else {
        Ok(())
    }
}

fn build(skip_npm: bool, production: bool) -> Result<()> {
    if !skip_npm {
        exec_should_success(Command::new("npm").args(&["add", "duniter-ui"]))?;
        exec_should_success(
            Command::new("npm")
                .env("NEON_BUILD_RELEASE", "true")
                .arg("install"),
        )?;
        if production {
            exec_should_success(Command::new("npm").args(&["prune", "--production"]))?;
        }
    }
    println!("Build duniter-launcher …");
    exec_should_success(Command::new("cargo").args(&["build", "--release"]))?;
    std::fs::copy("target/release/duniter", "bin/duniter")?;
    Ok(())
}

fn test(skip_npm: bool) -> Result<()> {
    exec_should_success(Command::new("cargo").args(&["test", "--all"]))?;
    if !skip_npm {
        exec_should_success(Command::new("npm").arg("test"))?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use crate::{
    ast::VariableDefinition,
    parser::Spanning,
    types::utilities::is_valid_literal_value,
    validation::{ValidatorContext, Visitor},
    value::ScalarValue,
};

pub struct DefaultValuesOfCorrectType;

pub fn factory() -> DefaultValuesOfCorrectType {
    DefaultValuesOfCorrectType
}

impl<'a, S> Visitor<'a, S> for DefaultValuesOfCorrectType
where
    S: ScalarValue,
{
    fn enter_variable_definition(
        &mut self,
        ctx: &mut ValidatorContext<'a, S>,
        &(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition<S>),
    ) {
        if let Some(Spanning {
            item: ref var_value,
            ref start,
            ..
        }) = var_def.default_value
        {
            if var_def.var_type.item.is_non_null() {
                ctx.report_error(
                    &non_null_error_message(var_name.item, &format!("{}", var_def.var_type.item)),
                    &[*start],
                )
            } else {
                let meta_type = ctx.schema.make_type(&var_def.var_type.item);

                if !is_valid_literal_value(ctx.schema, &meta_type, var_value) {
                    ctx.report_error(
                        &type_error_message(var_name.item, &format!("{}", var_def.var_type.item)),
                        &[*start],
                    );
                }
            }
        }
    }
}

fn type_error_message(arg_name: &str, type_name: &str) -> String {
    format!(
        "Invalid default value for argument \"{}\", expected type \"{}\"",
        arg_name, type_name
    )
}

fn non_null_error_message(arg_name: &str, type_name: &str) -> String {
    format!(
        "Argument \"{}\" has type \"{}\" and is not nullable, so it't can't have a default value",
        arg_name, type_name
    )
}

#[cfg(test)]
mod tests {
    use super::{factory, non_null_error_message, type_error_message};

    use crate::{
        parser::SourcePosition,
        validation::{expect_fails_rule, expect_passes_rule, RuleError},
        value::DefaultScalarValue,
    };

    #[test]
    fn variables_with_no_default_values() {
        expect_passes_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query NullableValues($a: Int, $b: String, $c: ComplexInput) {
            dog { name }
          }
        "#,
        );
    }

    #[test]
    fn required_variables_without_default_values() {
        expect_passes_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query RequiredValues($a: Int!, $b: String!) {
            dog { name }
          }
        "#,
        );
    }

    #[test]
    fn variables_with_valid_default_values() {
        expect_passes_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query WithDefaultValues(
            $a: Int = 1,
            $b: String = "ok",
            $c: ComplexInput = { requiredField: true, intField: 3 }
          ) {
            dog { name }
          }
        "#,
        );
    }

    #[test]
    fn no_required_variables_with_default_values() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") {
            dog { name }
          }
        "#,
            &[
                RuleError::new(
                    &non_null_error_message("a", "Int!"),
                    &[SourcePosition::new(53, 1, 52)],
                ),
                RuleError::new(
                    &non_null_error_message("b", "String!"),
                    &[SourcePosition::new(70, 1, 69)],
                ),
            ],
        );
    }

    #[test]
    fn variables_with_invalid_default_values() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query InvalidDefaultValues(
            $a: Int = "one",
            $b: String = 4,
            $c: ComplexInput = "notverycomplex"
          ) {
            dog { name }
          }
        "#,
            &[
                RuleError::new(
                    &type_error_message("a", "Int"),
                    &[SourcePosition::new(61, 2, 22)],
                ),
                RuleError::new(
                    &type_error_message("b", "String"),
                    &[SourcePosition::new(93, 3, 25)],
                ),
                RuleError::new(
                    &type_error_message("c", "ComplexInput"),
                    &[SourcePosition::new(127, 4, 31)],
                ),
            ],
        );
    }

    #[test]
    fn complex_variables_missing_required_field() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query MissingRequiredField($a: ComplexInput = {intField: 3}) {
            dog { name }
          }
        "#,
            &[RuleError::new(
                &type_error_message("a", "ComplexInput"),
                &[SourcePosition::new(57, 1, 56)],
            )],
        );
    }

    #[test]
    fn list_variables_with_invalid_item() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query InvalidItem($a: [String] = ["one", 2]) {
            dog { name }
          }
        "#,
            &[RuleError::new(
                &type_error_message("a", "[String]"),
                &[SourcePosition::new(44, 1, 43)],
            )],
        );
    }
}
<commit_msg>Fix typo (#866)<commit_after>use crate::{
    ast::VariableDefinition,
    parser::Spanning,
    types::utilities::is_valid_literal_value,
    validation::{ValidatorContext, Visitor},
    value::ScalarValue,
};

pub struct DefaultValuesOfCorrectType;

pub fn factory() -> DefaultValuesOfCorrectType {
    DefaultValuesOfCorrectType
}

impl<'a, S> Visitor<'a, S> for DefaultValuesOfCorrectType
where
    S: ScalarValue,
{
    fn enter_variable_definition(
        &mut self,
        ctx: &mut ValidatorContext<'a, S>,
        &(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition<S>),
    ) {
        if let Some(Spanning {
            item: ref var_value,
            ref start,
            ..
        }) = var_def.default_value
        {
            if var_def.var_type.item.is_non_null() {
                ctx.report_error(
                    &non_null_error_message(var_name.item, &format!("{}", var_def.var_type.item)),
                    &[*start],
                )
            } else {
                let meta_type = ctx.schema.make_type(&var_def.var_type.item);

                if !is_valid_literal_value(ctx.schema, &meta_type, var_value) {
                    ctx.report_error(
                        &type_error_message(var_name.item, &format!("{}", var_def.var_type.item)),
                        &[*start],
                    );
                }
            }
        }
    }
}

fn type_error_message(arg_name: &str, type_name: &str) -> String {
    format!(
        "Invalid default value for argument \"{}\", expected type \"{}\"",
        arg_name, type_name
    )
}

fn non_null_error_message(arg_name: &str, type_name: &str) -> String {
    format!(
        "Argument \"{}\" has type \"{}\" and is not nullable, so it can't have a default value",
        arg_name, type_name
    )
}

#[cfg(test)]
mod tests {
    use super::{factory, non_null_error_message, type_error_message};

    use crate::{
        parser::SourcePosition,
        validation::{expect_fails_rule, expect_passes_rule, RuleError},
        value::DefaultScalarValue,
    };

    #[test]
    fn variables_with_no_default_values() {
        expect_passes_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query NullableValues($a: Int, $b: String, $c: ComplexInput) {
            dog { name }
          }
        "#,
        );
    }

    #[test]
    fn required_variables_without_default_values() {
        expect_passes_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query RequiredValues($a: Int!, $b: String!) {
            dog { name }
          }
        "#,
        );
    }

    #[test]
    fn variables_with_valid_default_values() {
        expect_passes_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query WithDefaultValues(
            $a: Int = 1,
            $b: String = "ok",
            $c: ComplexInput = { requiredField: true, intField: 3 }
          ) {
            dog { name }
          }
        "#,
        );
    }

    #[test]
    fn no_required_variables_with_default_values() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") {
            dog { name }
          }
        "#,
            &[
                RuleError::new(
                    &non_null_error_message("a", "Int!"),
                    &[SourcePosition::new(53, 1, 52)],
                ),
                RuleError::new(
                    &non_null_error_message("b", "String!"),
                    &[SourcePosition::new(70, 1, 69)],
                ),
            ],
        );
    }

    #[test]
    fn variables_with_invalid_default_values() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query InvalidDefaultValues(
            $a: Int = "one",
            $b: String = 4,
            $c: ComplexInput = "notverycomplex"
          ) {
            dog { name }
          }
        "#,
            &[
                RuleError::new(
                    &type_error_message("a", "Int"),
                    &[SourcePosition::new(61, 2, 22)],
                ),
                RuleError::new(
                    &type_error_message("b", "String"),
                    &[SourcePosition::new(93, 3, 25)],
                ),
                RuleError::new(
                    &type_error_message("c", "ComplexInput"),
                    &[SourcePosition::new(127, 4, 31)],
                ),
            ],
        );
    }

    #[test]
    fn complex_variables_missing_required_field() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query MissingRequiredField($a: ComplexInput = {intField: 3}) {
            dog { name }
          }
        "#,
            &[RuleError::new(
                &type_error_message("a", "ComplexInput"),
                &[SourcePosition::new(57, 1, 56)],
            )],
        );
    }

    #[test]
    fn list_variables_with_invalid_item() {
        expect_fails_rule::<_, _, DefaultScalarValue>(
            factory,
            r#"
          query InvalidItem($a: [String] = ["one", 2]) {
            dog { name }
          }
        "#,
            &[RuleError::new(
                &type_error_message("a", "[String]"),
                &[SourcePosition::new(44, 1, 43)],
            )],
        );
    }
}
<|endoftext|>


<commit_before>pub fn is_valid(id: &str) -> bool {
    // check the length
    if id.len() < 2 {
        return false;
    }

    let mut index: u32 = 0;
    let mut number: Vec<u32> = Vec::new();

    for c in id.chars().rev() {
        if c.is_whitespace() {
            continue;
        }

        let mut v: u32;

        match c.to_digit(10) {
            Some(d) => v = d,
            None => return false,
        }

        if index % 2 != 0 {
            v *= 2;

            if v > 9 {
                v -= 9;
            }

        }

        index += 1;

        number.push(v);
    }

    if number.len() == 1 {
        return false;
    }

    let r: u32 = number.iter().sum();

    (r % 10) == 0
}
<commit_msg>Change luhn code to simplify<commit_after>pub fn is_valid(id: &str) -> bool {
    // check the length
    if id.len() < 2 {
        return false;
    }

    let mut index: u32 = 0;
    let mut sum: u32 = 0;

    for c in id.chars().rev() {
        if c.is_whitespace() {
            continue;
        }

        let mut v: u32;

        match c.to_digit(10) {
            Some(d) => v = d,
            None => return false,
        }

        if index % 2 != 0 {
            v *= 2;

            if v > 9 {
                v -= 9;
            }

        }

        index += 1;
        sum += v;
    }

    if index < 2 {
        return false;
    }

    (sum % 10) == 0
}
<|endoftext|>


<commit_before>use crate::spec::{FramePointer, LinkerFlavor, SanitizerSet, Target, TargetOptions};

pub fn target() -> Target {
    let mut base = super::apple_base::opts("macos");
    base.cpu = "apple-a14".to_string();
    base.max_atomic_width = Some(128);

    // FIXME: The leak sanitizer currently fails the tests, see #88132.
    base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD;

    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]);
    base.link_env_remove.extend(super::apple_base::macos_link_env_remove());

    // Clang automatically chooses a more specific target based on
    // MACOSX_DEPLOYMENT_TARGET.  To enable cross-language LTO to work
    // correctly, we do too.
    let arch = "aarch64";
    let llvm_target = super::apple_base::macos_llvm_target(&arch);

    Target {
        llvm_target,
        pointer_width: 64,
        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(),
        arch: arch.to_string(),
        options: TargetOptions {
            mcount: "\u{1}mcount".to_string(),
            frame_pointer: FramePointer::NonLeaf,
            ..base
        },
    }
}
<commit_msg>The correct LLVM target for aarch64-apple-darwin is arm64-... (as with ios)<commit_after>use crate::spec::{FramePointer, LinkerFlavor, SanitizerSet, Target, TargetOptions};

pub fn target() -> Target {
    let mut base = super::apple_base::opts("macos");
    base.cpu = "apple-a14".to_string();
    base.max_atomic_width = Some(128);

    // FIXME: The leak sanitizer currently fails the tests, see #88132.
    base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD;

    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]);
    base.link_env_remove.extend(super::apple_base::macos_link_env_remove());

    // Clang automatically chooses a more specific target based on
    // MACOSX_DEPLOYMENT_TARGET.  To enable cross-language LTO to work
    // correctly, we do too.
    let llvm_target = super::apple_base::macos_llvm_target("arm64");

    Target {
        llvm_target,
        pointer_width: 64,
        data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(),
        arch: "aarch64".to_string(),
        options: TargetOptions {
            mcount: "\u{1}mcount".to_string(),
            frame_pointer: FramePointer::NonLeaf,
            ..base
        },
    }
}
<|endoftext|>


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

<%! from data import to_rust_ident %>
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>

<% data.new_style_struct("Position", inherited=False) %>

// "top" / "left" / "bottom" / "right"
% for side in PHYSICAL_SIDES:
    ${helpers.predefined_type(
        side,
        "LengthPercentageOrAuto",
        "computed::LengthPercentageOrAuto::auto()",
        spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        animation_value_type="ComputedValue",
        allow_quirks=True,
        servo_restyle_damage="reflow_out_of_flow",
        logical_group="inset",
    )}
% endfor
// inset-* logical properties, map to "top" / "left" / "bottom" / "right"
% for side in LOGICAL_SIDES:
    ${helpers.predefined_type(
        "inset-%s" % side,
        "LengthPercentageOrAuto",
        "computed::LengthPercentageOrAuto::auto()",
        spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
        animation_value_type="ComputedValue",
        logical=True,
        logical_group="inset",
    )}
% endfor

#[cfg(feature = "gecko")]
macro_rules! impl_align_conversions {
    ($name: path) => {
        impl From<u8> for $name {
            fn from(bits: u8) -> $name {
                $name(crate::values::specified::align::AlignFlags::from_bits(bits)
                      .expect("bits contain valid flag"))
            }
        }

        impl From<$name> for u8 {
            fn from(v: $name) -> u8 {
                v.0.bits()
            }
        }
    };
}

${helpers.predefined_type(
    "z-index",
    "ZIndex",
    "computed::ZIndex::auto()",
    spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
    flags="CREATES_STACKING_CONTEXT",
    animation_value_type="ComputedValue",
)}

// CSS Flexible Box Layout Module Level 1
// http://www.w3.org/TR/css3-flexbox/

// Flex container properties
${helpers.single_keyword(
    "flex-direction",
    "row row-reverse column column-reverse",
    spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    servo_restyle_damage = "reflow",
)}

${helpers.single_keyword(
    "flex-wrap",
    "nowrap wrap wrap-reverse",
    spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    servo_restyle_damage = "reflow",
)}

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword(
        "justify-content",
        "flex-start stretch flex-end center space-between space-around",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
        animation_value_type="discrete",
        servo_restyle_damage = "reflow",
    )}
% else:
    ${helpers.predefined_type(
        "justify-content",
        "JustifyContent",
        "specified::JustifyContent(specified::ContentDistribution::normal())",
        spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
        extra_prefixes="webkit",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}
% endif

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword(
        "align-content",
        "stretch flex-start flex-end center space-between space-around",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-align/#propdef-align-content",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}

    ${helpers.single_keyword(
        "align-items",
        "stretch flex-start flex-end center baseline",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}
% else:
    ${helpers.predefined_type(
        "align-content",
        "AlignContent",
        "specified::AlignContent(specified::ContentDistribution::normal())",
        spec="https://drafts.csswg.org/css-align/#propdef-align-content",
        extra_prefixes="webkit",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}

    ${helpers.predefined_type(
        "align-items",
        "AlignItems",
        "specified::AlignItems::normal()",
        spec="https://drafts.csswg.org/css-align/#propdef-align-items",
        extra_prefixes="webkit",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}

    #[cfg(feature = "gecko")]
    impl_align_conversions!(crate::values::specified::align::AlignItems);

    ${helpers.predefined_type(
        "justify-items",
        "JustifyItems",
        "computed::JustifyItems::legacy()",
        spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
        animation_value_type="discrete",
    )}

    #[cfg(feature = "gecko")]
    impl_align_conversions!(crate::values::specified::align::JustifyItems);
% endif

// Flex item properties
${helpers.predefined_type(
    "flex-grow",
    "NonNegativeNumber",
    "From::from(0.0)",
    spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
    extra_prefixes="webkit",
    animation_value_type="NonNegativeNumber",
    servo_restyle_damage="reflow",
)}

${helpers.predefined_type(
    "flex-shrink",
    "NonNegativeNumber",
    "From::from(1.0)",
    spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
    extra_prefixes="webkit",
    animation_value_type="NonNegativeNumber",
    servo_restyle_damage = "reflow",
)}

// https://drafts.csswg.org/css-align/#align-self-property
% if product == "servo":
    // FIXME: Update Servo to support the same syntax as Gecko.
    ${helpers.single_keyword(
        "align-self",
        "auto stretch flex-start flex-end center baseline",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
        animation_value_type="discrete",
        servo_restyle_damage = "reflow",
    )}
% else:
    ${helpers.predefined_type(
        "align-self",
        "AlignSelf",
        "specified::AlignSelf(specified::SelfAlignment::auto())",
        spec="https://drafts.csswg.org/css-align/#align-self-property",
        extra_prefixes="webkit",
        animation_value_type="discrete",
    )}

    ${helpers.predefined_type(
        "justify-self",
        "JustifySelf",
        "specified::JustifySelf(specified::SelfAlignment::auto())",
        spec="https://drafts.csswg.org/css-align/#justify-self-property",
        animation_value_type="discrete",
    )}

    #[cfg(feature = "gecko")]
    impl_align_conversions!(crate::values::specified::align::SelfAlignment);
% endif

// https://drafts.csswg.org/css-flexbox/#propdef-order
${helpers.predefined_type(
    "order",
    "Integer",
    "0",
    extra_prefixes="webkit",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-flexbox/#order-property",
    servo_restyle_damage="reflow",
)}

${helpers.predefined_type(
    "flex-basis",
    "FlexBasis",
    "computed::FlexBasis::auto()",
    spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
    extra_prefixes="webkit",
    animation_value_type="FlexBasis",
    servo_restyle_damage="reflow",
    boxed=True,
)}

% for (size, logical) in ALL_SIZES:
    <%
        spec = "https://drafts.csswg.org/css-box/#propdef-%s"
        if logical:
            spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
    %>
    // width, height, block-size, inline-size
    ${helpers.predefined_type(
        size,
        "Size",
        "computed::Size::auto()",
        logical=logical,
        logical_group="size",
        allow_quirks=not logical,
        spec=spec % size,
        animation_value_type="Size",
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        servo_restyle_damage="reflow",
    )}
    // min-width, min-height, min-block-size, min-inline-size
    ${helpers.predefined_type(
        "min-%s" % size,
        "Size",
        "computed::Size::auto()",
        logical=logical,
        logical_group="min-size",
        allow_quirks=not logical,
        spec=spec % size,
        animation_value_type="Size",
        servo_restyle_damage="reflow",
    )}
    ${helpers.predefined_type(
        "max-%s" % size,
        "MaxSize",
        "computed::MaxSize::none()",
        logical=logical,
        logical_group="max-size",
        allow_quirks=not logical,
        spec=spec % size,
        animation_value_type="MaxSize",
        servo_restyle_damage="reflow",
    )}
% endfor

${helpers.single_keyword(
    "box-sizing",
    "content-box border-box",
    extra_prefixes="moz:layout.css.prefixes.box-sizing webkit",
    spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
    gecko_enum_prefix="StyleBoxSizing",
    custom_consts={ "content-box": "Content", "border-box": "Border" },
    animation_value_type="discrete",
    servo_restyle_damage = "reflow",
)}

${helpers.single_keyword(
    "object-fit",
    "fill contain cover none scale-down",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-images/#propdef-object-fit",
)}

${helpers.predefined_type(
    "object-position",
    "Position",
    "computed::Position::zero()",
    products="gecko",
    boxed=True,
    spec="https://drafts.csswg.org/css-images-3/#the-object-position",
    animation_value_type="ComputedValue",
)}

% for kind in ["row", "column"]:
    % for range in ["start", "end"]:
        ${helpers.predefined_type(
            "grid-%s-%s" % (kind, range),
            "GridLine",
            "Default::default()",
            animation_value_type="discrete",
            spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
            products="gecko",
            boxed=True,
        )}
    % endfor

    // NOTE: According to the spec, this should handle multiple values of `<track-size>`,
    // but gecko supports only a single value
    ${helpers.predefined_type(
        "grid-auto-%ss" % kind,
        "TrackSize",
        "Default::default()",
        animation_value_type="discrete",
        spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
        products="gecko",
        boxed=True,
    )}

    ${helpers.predefined_type(
        "grid-template-%ss" % kind,
        "GridTemplateComponent",
        "specified::GenericGridTemplateComponent::None",
        products="gecko",
        spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind,
        boxed=True,
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        animation_value_type="ComputedValue",
    )}

% endfor

${helpers.predefined_type(
    "grid-auto-flow",
    "GridAutoFlow",
    "computed::GridAutoFlow::row()",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow",
)}

${helpers.predefined_type(
    "grid-template-areas",
    "GridTemplateAreas",
    "computed::GridTemplateAreas::none()",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-areas",
)}

${helpers.predefined_type(
    "column-gap",
    "length::NonNegativeLengthPercentageOrNormal",
    "Either::Second(Normal)",
    alias="grid-column-gap" if product == "gecko" else "",
    extra_prefixes="moz",
    servo_pref="layout.columns.enabled",
    spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
    animation_value_type="NonNegativeLengthPercentageOrNormal",
    servo_restyle_damage="reflow",
)}

// no need for -moz- prefixed alias for this property
${helpers.predefined_type(
    "row-gap",
    "length::NonNegativeLengthPercentageOrNormal",
    "Either::Second(Normal)",
    alias="grid-row-gap",
    products="gecko",
    spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap",
    animation_value_type="NonNegativeLengthPercentageOrNormal",
    servo_restyle_damage="reflow",
)}
<commit_msg>style: Convert NS_STYLE_FLEX_DIRECTION to an enum class in nsStyleConsts.h<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%! from data import to_rust_ident %>
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>

<% data.new_style_struct("Position", inherited=False) %>

// "top" / "left" / "bottom" / "right"
% for side in PHYSICAL_SIDES:
    ${helpers.predefined_type(
        side,
        "LengthPercentageOrAuto",
        "computed::LengthPercentageOrAuto::auto()",
        spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        animation_value_type="ComputedValue",
        allow_quirks=True,
        servo_restyle_damage="reflow_out_of_flow",
        logical_group="inset",
    )}
% endfor
// inset-* logical properties, map to "top" / "left" / "bottom" / "right"
% for side in LOGICAL_SIDES:
    ${helpers.predefined_type(
        "inset-%s" % side,
        "LengthPercentageOrAuto",
        "computed::LengthPercentageOrAuto::auto()",
        spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
        animation_value_type="ComputedValue",
        logical=True,
        logical_group="inset",
    )}
% endfor

#[cfg(feature = "gecko")]
macro_rules! impl_align_conversions {
    ($name: path) => {
        impl From<u8> for $name {
            fn from(bits: u8) -> $name {
                $name(crate::values::specified::align::AlignFlags::from_bits(bits)
                      .expect("bits contain valid flag"))
            }
        }

        impl From<$name> for u8 {
            fn from(v: $name) -> u8 {
                v.0.bits()
            }
        }
    };
}

${helpers.predefined_type(
    "z-index",
    "ZIndex",
    "computed::ZIndex::auto()",
    spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
    flags="CREATES_STACKING_CONTEXT",
    animation_value_type="ComputedValue",
)}

// CSS Flexible Box Layout Module Level 1
// http://www.w3.org/TR/css3-flexbox/

// Flex container properties
${helpers.single_keyword(
    "flex-direction",
    "row row-reverse column column-reverse",
    spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    servo_restyle_damage = "reflow",
    gecko_enum_prefix = "StyleFlexDirection",
)}

${helpers.single_keyword(
    "flex-wrap",
    "nowrap wrap wrap-reverse",
    spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    servo_restyle_damage = "reflow",
)}

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword(
        "justify-content",
        "flex-start stretch flex-end center space-between space-around",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
        animation_value_type="discrete",
        servo_restyle_damage = "reflow",
    )}
% else:
    ${helpers.predefined_type(
        "justify-content",
        "JustifyContent",
        "specified::JustifyContent(specified::ContentDistribution::normal())",
        spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
        extra_prefixes="webkit",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}
% endif

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword(
        "align-content",
        "stretch flex-start flex-end center space-between space-around",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-align/#propdef-align-content",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}

    ${helpers.single_keyword(
        "align-items",
        "stretch flex-start flex-end center baseline",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}
% else:
    ${helpers.predefined_type(
        "align-content",
        "AlignContent",
        "specified::AlignContent(specified::ContentDistribution::normal())",
        spec="https://drafts.csswg.org/css-align/#propdef-align-content",
        extra_prefixes="webkit",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}

    ${helpers.predefined_type(
        "align-items",
        "AlignItems",
        "specified::AlignItems::normal()",
        spec="https://drafts.csswg.org/css-align/#propdef-align-items",
        extra_prefixes="webkit",
        animation_value_type="discrete",
        servo_restyle_damage="reflow",
    )}

    #[cfg(feature = "gecko")]
    impl_align_conversions!(crate::values::specified::align::AlignItems);

    ${helpers.predefined_type(
        "justify-items",
        "JustifyItems",
        "computed::JustifyItems::legacy()",
        spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
        animation_value_type="discrete",
    )}

    #[cfg(feature = "gecko")]
    impl_align_conversions!(crate::values::specified::align::JustifyItems);
% endif

// Flex item properties
${helpers.predefined_type(
    "flex-grow",
    "NonNegativeNumber",
    "From::from(0.0)",
    spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
    extra_prefixes="webkit",
    animation_value_type="NonNegativeNumber",
    servo_restyle_damage="reflow",
)}

${helpers.predefined_type(
    "flex-shrink",
    "NonNegativeNumber",
    "From::from(1.0)",
    spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
    extra_prefixes="webkit",
    animation_value_type="NonNegativeNumber",
    servo_restyle_damage = "reflow",
)}

// https://drafts.csswg.org/css-align/#align-self-property
% if product == "servo":
    // FIXME: Update Servo to support the same syntax as Gecko.
    ${helpers.single_keyword(
        "align-self",
        "auto stretch flex-start flex-end center baseline",
        extra_prefixes="webkit",
        spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
        animation_value_type="discrete",
        servo_restyle_damage = "reflow",
    )}
% else:
    ${helpers.predefined_type(
        "align-self",
        "AlignSelf",
        "specified::AlignSelf(specified::SelfAlignment::auto())",
        spec="https://drafts.csswg.org/css-align/#align-self-property",
        extra_prefixes="webkit",
        animation_value_type="discrete",
    )}

    ${helpers.predefined_type(
        "justify-self",
        "JustifySelf",
        "specified::JustifySelf(specified::SelfAlignment::auto())",
        spec="https://drafts.csswg.org/css-align/#justify-self-property",
        animation_value_type="discrete",
    )}

    #[cfg(feature = "gecko")]
    impl_align_conversions!(crate::values::specified::align::SelfAlignment);
% endif

// https://drafts.csswg.org/css-flexbox/#propdef-order
${helpers.predefined_type(
    "order",
    "Integer",
    "0",
    extra_prefixes="webkit",
    animation_value_type="ComputedValue",
    spec="https://drafts.csswg.org/css-flexbox/#order-property",
    servo_restyle_damage="reflow",
)}

${helpers.predefined_type(
    "flex-basis",
    "FlexBasis",
    "computed::FlexBasis::auto()",
    spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
    extra_prefixes="webkit",
    animation_value_type="FlexBasis",
    servo_restyle_damage="reflow",
    boxed=True,
)}

% for (size, logical) in ALL_SIZES:
    <%
        spec = "https://drafts.csswg.org/css-box/#propdef-%s"
        if logical:
            spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
    %>
    // width, height, block-size, inline-size
    ${helpers.predefined_type(
        size,
        "Size",
        "computed::Size::auto()",
        logical=logical,
        logical_group="size",
        allow_quirks=not logical,
        spec=spec % size,
        animation_value_type="Size",
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        servo_restyle_damage="reflow",
    )}
    // min-width, min-height, min-block-size, min-inline-size
    ${helpers.predefined_type(
        "min-%s" % size,
        "Size",
        "computed::Size::auto()",
        logical=logical,
        logical_group="min-size",
        allow_quirks=not logical,
        spec=spec % size,
        animation_value_type="Size",
        servo_restyle_damage="reflow",
    )}
    ${helpers.predefined_type(
        "max-%s" % size,
        "MaxSize",
        "computed::MaxSize::none()",
        logical=logical,
        logical_group="max-size",
        allow_quirks=not logical,
        spec=spec % size,
        animation_value_type="MaxSize",
        servo_restyle_damage="reflow",
    )}
% endfor

${helpers.single_keyword(
    "box-sizing",
    "content-box border-box",
    extra_prefixes="moz:layout.css.prefixes.box-sizing webkit",
    spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
    gecko_enum_prefix="StyleBoxSizing",
    custom_consts={ "content-box": "Content", "border-box": "Border" },
    animation_value_type="discrete",
    servo_restyle_damage = "reflow",
)}

${helpers.single_keyword(
    "object-fit",
    "fill contain cover none scale-down",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-images/#propdef-object-fit",
)}

${helpers.predefined_type(
    "object-position",
    "Position",
    "computed::Position::zero()",
    products="gecko",
    boxed=True,
    spec="https://drafts.csswg.org/css-images-3/#the-object-position",
    animation_value_type="ComputedValue",
)}

% for kind in ["row", "column"]:
    % for range in ["start", "end"]:
        ${helpers.predefined_type(
            "grid-%s-%s" % (kind, range),
            "GridLine",
            "Default::default()",
            animation_value_type="discrete",
            spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
            products="gecko",
            boxed=True,
        )}
    % endfor

    // NOTE: According to the spec, this should handle multiple values of `<track-size>`,
    // but gecko supports only a single value
    ${helpers.predefined_type(
        "grid-auto-%ss" % kind,
        "TrackSize",
        "Default::default()",
        animation_value_type="discrete",
        spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
        products="gecko",
        boxed=True,
    )}

    ${helpers.predefined_type(
        "grid-template-%ss" % kind,
        "GridTemplateComponent",
        "specified::GenericGridTemplateComponent::None",
        products="gecko",
        spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-%ss" % kind,
        boxed=True,
        flags="GETCS_NEEDS_LAYOUT_FLUSH",
        animation_value_type="ComputedValue",
    )}

% endfor

${helpers.predefined_type(
    "grid-auto-flow",
    "GridAutoFlow",
    "computed::GridAutoFlow::row()",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow",
)}

${helpers.predefined_type(
    "grid-template-areas",
    "GridTemplateAreas",
    "computed::GridTemplateAreas::none()",
    products="gecko",
    animation_value_type="discrete",
    spec="https://drafts.csswg.org/css-grid/#propdef-grid-template-areas",
)}

${helpers.predefined_type(
    "column-gap",
    "length::NonNegativeLengthPercentageOrNormal",
    "Either::Second(Normal)",
    alias="grid-column-gap" if product == "gecko" else "",
    extra_prefixes="moz",
    servo_pref="layout.columns.enabled",
    spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
    animation_value_type="NonNegativeLengthPercentageOrNormal",
    servo_restyle_damage="reflow",
)}

// no need for -moz- prefixed alias for this property
${helpers.predefined_type(
    "row-gap",
    "length::NonNegativeLengthPercentageOrNormal",
    "Either::Second(Normal)",
    alias="grid-row-gap",
    products="gecko",
    spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap",
    animation_value_type="NonNegativeLengthPercentageOrNormal",
    servo_restyle_damage="reflow",
)}
<|endoftext|>


<commit_before>use crate::stream::{StreamExt, Fuse};
use core::pin::Pin;
use futures_core::future::{FusedFuture, Future};
use futures_core::stream::Stream;
use futures_core::task::{Waker, Poll};
use futures_sink::Sink;
use pin_utils::{unsafe_pinned, unsafe_unpinned};

const INVALID_POLL: &str = "polled `Forward` after completion";

/// Future for the `Stream::forward` combinator, which sends a stream of values
/// to a sink and then flushes the sink.
///
/// Note: this is only usable with `Unpin` sinks, so `Sink`s that aren't `Unpin`
/// will need to be pinned in order to be used with this combinator.
//
// This limitation is necessary in order to return the sink after the forwarding
// has completed so that it can be used again.
#[derive(Debug)]
#[must_use = "steams do nothing unless polled"]
pub struct Forward<St: Stream, Si: Sink + Unpin> {
    sink: Option<Si>,
    stream: Fuse<St>,
    buffered_item: Option<Si::SinkItem>,
}

impl<St: Stream + Unpin, Si: Sink + Unpin> Unpin for Forward<St, Si> {}

impl<St, Si> Forward<St, Si>
where
    Si: Sink + Unpin,
    St: Stream<Item = Result<Si::SinkItem, Si::SinkError>>,
{
    unsafe_pinned!(sink: Option<Si>);
    unsafe_pinned!(stream: Fuse<St>);
    unsafe_unpinned!(buffered_item: Option<Si::SinkItem>);

    pub(super) fn new(stream: St, sink: Si) -> Forward<St, Si> {
        Forward {
            sink: Some(sink),
            stream: stream.fuse(),
                buffered_item: None,
        }
    }

    fn try_start_send(
        mut self: Pin<&mut Self>,
        waker: &Waker,
        item: Si::SinkItem,
    ) -> Poll<Result<(), Si::SinkError>> {
        debug_assert!(self.buffered_item.is_none());
        {
            let mut sink = self.as_mut().sink().as_pin_mut().unwrap();
            if sink.as_mut().poll_ready(waker)?.is_ready() {
                return Poll::Ready(sink.start_send(item));
            }
        }
        *self.as_mut().buffered_item() = Some(item);
        Poll::Pending
    }
}

impl<St: Stream, Si: Sink + Unpin> FusedFuture for Forward<St, Si> {
    fn is_terminated(&self) -> bool {
        self.sink.is_none()
    }
}

impl<St, Si> Future for Forward<St, Si>
where
    Si: Sink + Unpin,
    St: Stream<Item = Result<Si::SinkItem, Si::SinkError>>,
{
    type Output = Result<(), Si::SinkError>;

    fn poll(
        mut self: Pin<&mut Self>,
        waker: &Waker,
    ) -> Poll<Self::Output> {
        // If we've got an item buffered already, we need to write it to the
        // sink before we can do anything else
        if let Some(item) = self.as_mut().buffered_item().take() {
            try_ready!(self.as_mut().try_start_send(waker, item));
        }

        loop {
            match self.as_mut().stream().poll_next(waker) {
                Poll::Ready(Some(Ok(item))) =>
                   try_ready!(self.as_mut().try_start_send(waker, item)),
                Poll::Ready(Some(Err(e))) => return Poll::Ready(Err(e)),
                Poll::Ready(None) => {
                    try_ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL)
                                   .poll_close(waker));
                    let _ = self.as_mut().sink().take().unwrap();
                    return Poll::Ready(Ok(()))
                }
                Poll::Pending => {
                    try_ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL)
                                   .poll_flush(waker));
                    return Poll::Pending
                }
            }
        }
    }
}
<commit_msg>Update Forward docs, remove Unpin sink requirement<commit_after>use crate::stream::{StreamExt, Fuse};
use core::pin::Pin;
use futures_core::future::{FusedFuture, Future};
use futures_core::stream::Stream;
use futures_core::task::{Waker, Poll};
use futures_sink::Sink;
use pin_utils::{unsafe_pinned, unsafe_unpinned};

const INVALID_POLL: &str = "polled `Forward` after completion";

/// Future for the `Stream::forward` combinator, which sends a stream of values
/// to a sink and then flushes and closes the sink.
#[derive(Debug)]
#[must_use = "steams do nothing unless polled"]
pub struct Forward<St: Stream, Si: Sink> {
    sink: Option<Si>,
    stream: Fuse<St>,
    buffered_item: Option<Si::SinkItem>,
}

impl<St: Stream + Unpin, Si: Sink + Unpin> Unpin for Forward<St, Si> {}

impl<St, Si> Forward<St, Si>
where
    Si: Sink,
    St: Stream<Item = Result<Si::SinkItem, Si::SinkError>>,
{
    unsafe_pinned!(sink: Option<Si>);
    unsafe_pinned!(stream: Fuse<St>);
    unsafe_unpinned!(buffered_item: Option<Si::SinkItem>);

    pub(super) fn new(stream: St, sink: Si) -> Forward<St, Si> {
        Forward {
            sink: Some(sink),
            stream: stream.fuse(),
                buffered_item: None,
        }
    }

    fn try_start_send(
        mut self: Pin<&mut Self>,
        waker: &Waker,
        item: Si::SinkItem,
    ) -> Poll<Result<(), Si::SinkError>> {
        debug_assert!(self.buffered_item.is_none());
        {
            let mut sink = self.as_mut().sink().as_pin_mut().unwrap();
            if sink.as_mut().poll_ready(waker)?.is_ready() {
                return Poll::Ready(sink.start_send(item));
            }
        }
        *self.as_mut().buffered_item() = Some(item);
        Poll::Pending
    }
}

impl<St: Stream, Si: Sink + Unpin> FusedFuture for Forward<St, Si> {
    fn is_terminated(&self) -> bool {
        self.sink.is_none()
    }
}

impl<St, Si> Future for Forward<St, Si>
where
    Si: Sink,
    St: Stream<Item = Result<Si::SinkItem, Si::SinkError>>,
{
    type Output = Result<(), Si::SinkError>;

    fn poll(
        mut self: Pin<&mut Self>,
        waker: &Waker,
    ) -> Poll<Self::Output> {
        // If we've got an item buffered already, we need to write it to the
        // sink before we can do anything else
        if let Some(item) = self.as_mut().buffered_item().take() {
            try_ready!(self.as_mut().try_start_send(waker, item));
        }

        loop {
            match self.as_mut().stream().poll_next(waker) {
                Poll::Ready(Some(Ok(item))) =>
                   try_ready!(self.as_mut().try_start_send(waker, item)),
                Poll::Ready(Some(Err(e))) => return Poll::Ready(Err(e)),
                Poll::Ready(None) => {
                    try_ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL)
                                   .poll_close(waker));
                    let _ = self.as_mut().sink().as_pin_mut().take().unwrap();
                    return Poll::Ready(Ok(()))
                }
                Poll::Pending => {
                    try_ready!(self.as_mut().sink().as_pin_mut().expect(INVALID_POLL)
                                   .poll_flush(waker));
                    return Poll::Pending
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use raw::Local;
use cslice::CMutSlice;

// Suppress a spurious rustc warning about the use of CMutSlice.
#[allow(improper_ctypes)]
extern "system" {

    #[link_name = "NeonSys_Buffer_New"]
    pub fn new(out: &mut Local, size: u32) -> bool;

    #[link_name = "NeonSys_Buffer_Data"]
    pub fn data<'a, 'b>(out: &'a mut CMutSlice<'b, u8>, obj: Local);

}
<commit_msg>expand neon_sys::buffer mod documentation<commit_after>//! Facilities for working with `node::Buffer`s.

use raw::Local;
use cslice::CMutSlice;

// Suppress a spurious rustc warning about the use of CMutSlice.
#[allow(improper_ctypes)]
extern "system" {

    /// Mutates the `out` argument provided to refer to a newly created `node::Buffer` object.
    /// Returns `false` if the value couldn't be created.
    #[link_name = "NeonSys_Buffer_New"]
    pub fn new(out: &mut Local, size: u32) -> bool;

    /// Mutates the `out` argument provided populating the `data` and `len` properties.
    #[link_name = "NeonSys_Buffer_Data"]
    pub fn data<'a, 'b>(out: &'a mut CMutSlice<'b, u8>, obj: Local);

}
<|endoftext|>