<commit_before>//! Module that implements constant propagation on the SSA IR.
//!
//! This module implements 'Sparse Conditional Constant' algorithm to
//! perform the propagation. For more details, please refer:
//!    * https://www.cs.utexas.edu/~lin/cs380c/wegman.pdf.
//!

use petgraph::graph::{NodeIndex};
use std::collections::{HashMap};

#[derive(Copy, Clone, Debug, PartialEq)]
enum ExprVal {
    Top,
    Bottom,
    Const(i64),
}

fn meet(v1: &ExprVal, v2: &ExprVal) -> ExprVal {
    // Any ^ Top    = Any
    // Any ^ Bottom = Bottom
    //   C ^ C      = C      (C = Constant)
    //   C ^ D      = Bottom (C, D = Constant and C != D).

    match *v1 {
        ExprVal::Top    => return *v2,
        ExprVal::Bottom => return ExprVal::Bottom,
        _               => { },
    }

    match *v2 {
        ExprVal::Top    => return *v1,
        ExprVal::Bottom => return ExprVal::Bottom,
        _               => { },
    }

    if *v1 != *v2 {
        return ExprVal::Bottom;
    }

    return *v1;
}

pub trait SSAGraphIter {
    fn get_blocks(&self) -> Vec<NodeIndex>;
    fn start_node(&self) -> NodeIndex;
    fn get_exprs(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn get_phis(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn get_uses(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn get_def(&self, i: &NodeIndex) -> NodeIndex;
    fn get_block(&self, i: &NodeIndex) -> NodeIndex;
    fn get_ops(&self, i: &NodeIndex) -> NodeIndex;
    fn get_operands(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn lhs(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn rhs(&self, i: &NodeIndex) -> Vec<NodeIndex>;
}

pub struct Analyzer<T: SSAGraphIter + Clone> {
    ssa_worklist: Vec<NodeIndex>,
    cfg_worklist: Vec<NodeIndex>,
    executable: HashMap<NodeIndex, bool>,
    expr_val: HashMap<NodeIndex, ExprVal>,
    g: T,
}

impl<T: SSAGraphIter + Clone> Analyzer<T> {
    pub fn new(g: &mut T) -> Analyzer<T> {
        Analyzer {
            ssa_worklist: Vec::new(),
            cfg_worklist: Vec::new(),
            executable: HashMap::new(),
            expr_val: HashMap::new(),
            g: g.clone(),
        }
    }

    pub fn visit_phi(&mut self, i: &NodeIndex) {
        let operands = self.g.get_operands(i);
        let mut phi_val = self.expr_val.get(i).unwrap().clone();
        //let old_val = phi_val;
        for o in operands.iter() {
            //let def = self.get_def(o);
            let b = self.g.get_block(&o);
            let val = match *self.executable.get(&b).unwrap() {
                true => *self.expr_val.get(&o).unwrap(),
                false => ExprVal::Top,
            };
            phi_val = meet(&phi_val, &val);
        }
        *(self.expr_val.get_mut(i).unwrap()) = phi_val;
    }

    pub fn visit_expression(&mut self, i: NodeIndex) {
    }

    pub fn analyze(&mut self) {
        // Initializations
        self.cfg_worklist.push(self.g.start_node());
        for block in self.g.get_blocks().iter() {
            self.executable.insert(block.clone(), false);
            for expr in self.g.get_exprs(block) {
                self.expr_val.insert(expr.clone(), ExprVal::Top);
            }
        }

        // Iterative worklist.
        while self.ssa_worklist.len() > 0 || self.cfg_worklist.len() > 0 {
            while self.cfg_worklist.len() > 0 {
                let block = self.cfg_worklist.pop().unwrap();
                *(self.executable.get_mut(&block).unwrap()) = true;
                let phis = self.g.get_phis(&block);
                // Evaluate phis
                for phi in phis.iter() {
                    // Visit the phi's and set their values.
                    self.visit_phi(phi);
                }
                // Iterate through all the expression in the block.
                for expr in self.g.get_exprs(&block) {
                    // TODO: Evaluate expr and update self.expr_val.
                    for _use in self.g.get_uses(&expr) {
                        let owner_block = self.g.get_block(&_use);
                        if *self.executable.get(&owner_block).unwrap() {
                            self.ssa_worklist.push(_use);
                        }
                    }
                }
            }

            while self.ssa_worklist.len() > 0 {
                let e = self.ssa_worklist.pop().unwrap();
                // self.get the operation/expression to which this operand belongs to.
                let op = self.g.get_ops(&e);
                // TODO: Evaluate the expression.
                let t = ExprVal::Top;
                if t != *self.expr_val.get(&op).unwrap() {
                    *(self.expr_val.get_mut(&op).unwrap()) = ExprVal::Top;
                    for _use in self.g.get_uses(&op).iter() {
                        let b = self.g.get_block(_use);
                        if *self.executable.get(&b).unwrap() {
                            self.ssa_worklist.push(_use.clone())
                        }
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::{ExprVal, meet};

    #[test]
    fn test_meet() {
        let t = ExprVal::Top;
        let b = ExprVal::Bottom;
        let c1 = ExprVal::Const(1);
        let c2 = ExprVal::Const(2);

        assert_eq!(meet(&t,  &t) , t);
        assert_eq!(meet(&t,  &b) , b);
        assert_eq!(meet(&t,  &c1), c1);
        assert_eq!(meet(&t,  &c2), c2);
        assert_eq!(meet(&c1, &b) , b);
        assert_eq!(meet(&c2, &c1), b);
        assert_eq!(meet(&c1, &c1), c1);
    }
}
<commit_msg>Stable. Started Visit-Expression<commit_after>//! Module that implements constant propagation on the SSA IR.
//!
//! This module implements 'Sparse Conditional Constant' algorithm to
//! perform the propagation. For more details, please refer:
//!    * https://www.cs.utexas.edu/~lin/cs380c/wegman.pdf.
//!

use petgraph::graph::{NodeIndex};
use std::collections::{HashMap};

#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ExprVal {
    Top,
    Bottom,
    Const(i64),
}

fn meet(v1: &ExprVal, v2: &ExprVal) -> ExprVal {
    // Any ^ Top    = Any
    // Any ^ Bottom = Bottom
    //   C ^ C      = C      (C = Constant)
    //   C ^ D      = Bottom (C, D = Constant and C != D).

    match *v1 {
        ExprVal::Top    => return *v2,
        ExprVal::Bottom => return ExprVal::Bottom,
        _               => { },
    }

    match *v2 {
        ExprVal::Top    => return *v1,
        ExprVal::Bottom => return ExprVal::Bottom,
        _               => { },
    }

    if *v1 != *v2 {
        return ExprVal::Bottom;
    }

    return *v1;
}

pub trait SSAGraphIter {
    fn get_blocks(&self) -> Vec<NodeIndex>;
    fn start_node(&self) -> NodeIndex;
    fn get_exprs(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn get_phis(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn get_uses(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn get_block(&self, i: &NodeIndex) -> NodeIndex;
    fn get_operands(&self, i: &NodeIndex) -> Vec<NodeIndex>;
    fn lhs(&self, i: &NodeIndex) -> NodeIndex;
    fn rhs(&self, i: &NodeIndex) -> NodeIndex;
}

pub struct Analyzer<T: SSAGraphIter + Clone> {
    ssa_worklist: Vec<NodeIndex>,
    cfg_worklist: Vec<NodeIndex>,
    executable: HashMap<NodeIndex, bool>,
    expr_val: HashMap<NodeIndex, ExprVal>,
    g: T,
}

impl<T: SSAGraphIter + Clone> Analyzer<T> {
    pub fn new(g: &mut T) -> Analyzer<T> {
        Analyzer {
            ssa_worklist: Vec::new(),
            cfg_worklist: Vec::new(),
            executable: HashMap::new(),
            expr_val: HashMap::new(),
            g: g.clone(),
        }
    }

    pub fn visit_phi(&mut self, i: &NodeIndex) -> ExprVal {
        let operands = self.g.get_operands(i);
        let mut phi_val = self.expr_val.get(i).unwrap().clone();
        for o in operands.iter() {
            let b = self.g.get_block(&o);
            let val = match *self.executable.get(&b).unwrap() {
                true => *self.expr_val.get(&o).unwrap(),
                false => ExprVal::Top,
            };
            phi_val = meet(&phi_val, &val);
        }

        return phi_val;
    }

    pub fn visit_expression(&mut self, i: &NodeIndex) -> ExprVal {
        ExprVal::Top
    }

    pub fn analyze(&mut self) {
        // Initializations
        self.cfg_worklist.push(self.g.start_node());
        for block in self.g.get_blocks().iter() {
            self.executable.insert(block.clone(), false);
            for expr in self.g.get_exprs(block) {
                self.expr_val.insert(expr.clone(), ExprVal::Top);
            }
        }

        // Iterative worklist.
        while self.ssa_worklist.len() > 0 || self.cfg_worklist.len() > 0 {
            while self.cfg_worklist.len() > 0 {
                let block = self.cfg_worklist.pop().unwrap();
                *(self.executable.get_mut(&block).unwrap()) = true;
                let phis = self.g.get_phis(&block);
                // Evaluate phis
                for phi in phis.iter() {
                    // Visit the phi's and set their values.
                    let v = self.visit_phi(phi);
                    *(self.expr_val.get_mut(phi).unwrap()) = v;
                }
                // Iterate through all the expression in the block.
                for expr in self.g.get_exprs(&block) {
                    let val = self.visit_expression(&expr);
                    *(self.expr_val.get_mut(&expr).unwrap()) = val;
                    for _use in self.g.get_uses(&expr) {
                        let owner_block = self.g.get_block(&_use);
                        if *self.executable.get(&owner_block).unwrap() {
                            self.ssa_worklist.push(_use);
                        }
                    }
                }
            }

            while self.ssa_worklist.len() > 0 {
                let e = self.ssa_worklist.pop().unwrap();
                // self.get the operation/expression to which this operand belongs to.
                let exprs = self.g.get_uses(&e);
                for expr in exprs.iter() {
                    let t = self.visit_expression(&expr);
                    if t != *self.expr_val.get(expr).unwrap() {
                        *(self.expr_val.get_mut(expr).unwrap()) = t;
                        for _use in self.g.get_uses(expr).iter() {
                            let b = self.g.get_block(_use);
                            if *self.executable.get(&b).unwrap() {
                                self.ssa_worklist.push(_use.clone())
                            }
                        }
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::{ExprVal, meet};

    #[test]
    fn test_meet() {
        let t = ExprVal::Top;
        let b = ExprVal::Bottom;
        let c1 = ExprVal::Const(1);
        let c2 = ExprVal::Const(2);

        assert_eq!(meet(&t,  &t) , t);
        assert_eq!(meet(&t,  &b) , b);
        assert_eq!(meet(&t,  &c1), c1);
        assert_eq!(meet(&t,  &c2), c2);
        assert_eq!(meet(&c1, &b) , b);
        assert_eq!(meet(&c2, &c1), b);
        assert_eq!(meet(&c1, &c1), c1);
    }
}
<|endoftext|>


<commit_before>use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use gio;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;

use crate::allowed_url::{AllowedUrl, Fragment};
use crate::error::LoadingError;
use crate::handle::LoadOptions;
use crate::io::{self, BinaryData};
use crate::node::{NodeCascade, NodeType, RsvgNode};
use crate::properties::ComputedValues;
use crate::structure::{IntrinsicDimensions, NodeSvg};
use crate::surface_utils::shared_surface::SharedImageSurface;
use crate::xml::xml_load_from_possibly_compressed_stream;

/// A loaded SVG file and its derived data
///
/// This contains the tree of nodes (SVG elements), the mapping
/// of id to node, and the CSS styles defined for this SVG.
pub struct Svg {
    tree: RsvgNode,

    ids: HashMap<String, RsvgNode>,

    // These require interior mutability because we load the extern
    // resources all over the place.  Eventually we'll be able to do this
    // once, at loading time, and keep this immutable.
    externs: RefCell<Resources>,
    images: RefCell<Images>,

    // Once we do not need to load externs, we can drop this as well
    load_options: LoadOptions,
}

impl Svg {
    pub fn new(
        mut tree: RsvgNode,
        ids: HashMap<String, RsvgNode>,
        load_options: LoadOptions,
    ) -> Svg {
        let values = ComputedValues::default();
        tree.cascade(&values);

        Svg {
            tree,
            ids,
            externs: RefCell::new(Resources::new()),
            images: RefCell::new(Images::new()),
            load_options,
        }
    }

    pub fn load_from_stream(
        load_options: &LoadOptions,
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Svg, LoadingError> {
        xml_load_from_possibly_compressed_stream(load_options, stream, cancellable)
    }

    pub fn root(&self) -> RsvgNode {
        self.tree.clone()
    }

    pub fn lookup(&self, fragment: &Fragment) -> Result<RsvgNode, LoadingError> {
        if fragment.uri().is_some() {
            self.externs
                .borrow_mut()
                .lookup(&self.load_options, fragment)
        } else {
            self.lookup_node_by_id(fragment.fragment())
                .ok_or(LoadingError::BadUrl)
        }
    }

    pub fn lookup_node_by_id(&self, id: &str) -> Option<RsvgNode> {
        self.ids.get(id).map(|n| (*n).clone())
    }

    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, LoadingError> {
        self.images.borrow_mut().lookup(&self.load_options, href)
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        let root = self.root();
        let node_data = root.borrow();

        assert!(node_data.get_type() == NodeType::Svg);
        node_data.get_impl::<NodeSvg>().get_intrinsic_dimensions()
    }
}

struct Resources {
    resources: HashMap<AllowedUrl, Result<Rc<Svg>, LoadingError>>,
}

impl Resources {
    pub fn new() -> Resources {
        Resources {
            resources: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        fragment: &Fragment,
    ) -> Result<RsvgNode, LoadingError> {
        if let Some(ref href) = fragment.uri() {
            self.get_extern_svg(load_options, href).and_then(|svg| {
                svg.lookup_node_by_id(fragment.fragment())
                    .ok_or(LoadingError::BadUrl)
            })
        } else {
            unreachable!();
        }
    }

    fn get_extern_svg(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<Rc<Svg>, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.resources.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let svg = load_svg(load_options, e.key()).map(|s| Rc::new(s));
                let res = e.insert(svg);
                res.clone()
            }
        }
    }
}

struct Images {
    images: HashMap<AllowedUrl, Result<SharedImageSurface, LoadingError>>,
}

impl Images {
    pub fn new() -> Images {
        Images {
            images: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<SharedImageSurface, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.images.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let surface = load_image(load_options, e.key());
                let res = e.insert(surface);
                res.clone()
            }
        }
    }
}

fn load_svg(load_options: &LoadOptions, aurl: &AllowedUrl) -> Result<Svg, LoadingError> {
    // FIXME: pass a cancellable to these
    io::acquire_stream(aurl, None).and_then(|stream| {
        Svg::load_from_stream(&load_options.copy_with_base_url(aurl), &stream, None)
    })
}

fn load_image(
    load_options: &LoadOptions,
    aurl: &AllowedUrl,
) -> Result<SharedImageSurface, LoadingError> {
    let BinaryData {
        data: bytes,
        content_type,
    } = io::acquire_data(&aurl, None)?;

    if bytes.len() == 0 {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&bytes)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let bytes = if load_options.keep_image_data {
        Some(bytes)
    } else {
        None
    };

    let surface =
        SharedImageSurface::from_pixbuf(&pixbuf, bytes, content_type.as_ref().map(String::as_str))?;

    Ok(surface)
}
<commit_msg>svg: cleanup map call<commit_after>use gdk_pixbuf::{PixbufLoader, PixbufLoaderExt};
use gio;
use std::cell::RefCell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::rc::Rc;

use crate::allowed_url::{AllowedUrl, Fragment};
use crate::error::LoadingError;
use crate::handle::LoadOptions;
use crate::io::{self, BinaryData};
use crate::node::{NodeCascade, NodeType, RsvgNode};
use crate::properties::ComputedValues;
use crate::structure::{IntrinsicDimensions, NodeSvg};
use crate::surface_utils::shared_surface::SharedImageSurface;
use crate::xml::xml_load_from_possibly_compressed_stream;

/// A loaded SVG file and its derived data
///
/// This contains the tree of nodes (SVG elements), the mapping
/// of id to node, and the CSS styles defined for this SVG.
pub struct Svg {
    tree: RsvgNode,

    ids: HashMap<String, RsvgNode>,

    // These require interior mutability because we load the extern
    // resources all over the place.  Eventually we'll be able to do this
    // once, at loading time, and keep this immutable.
    externs: RefCell<Resources>,
    images: RefCell<Images>,

    // Once we do not need to load externs, we can drop this as well
    load_options: LoadOptions,
}

impl Svg {
    pub fn new(
        mut tree: RsvgNode,
        ids: HashMap<String, RsvgNode>,
        load_options: LoadOptions,
    ) -> Svg {
        let values = ComputedValues::default();
        tree.cascade(&values);

        Svg {
            tree,
            ids,
            externs: RefCell::new(Resources::new()),
            images: RefCell::new(Images::new()),
            load_options,
        }
    }

    pub fn load_from_stream(
        load_options: &LoadOptions,
        stream: &gio::InputStream,
        cancellable: Option<&gio::Cancellable>,
    ) -> Result<Svg, LoadingError> {
        xml_load_from_possibly_compressed_stream(load_options, stream, cancellable)
    }

    pub fn root(&self) -> RsvgNode {
        self.tree.clone()
    }

    pub fn lookup(&self, fragment: &Fragment) -> Result<RsvgNode, LoadingError> {
        if fragment.uri().is_some() {
            self.externs
                .borrow_mut()
                .lookup(&self.load_options, fragment)
        } else {
            self.lookup_node_by_id(fragment.fragment())
                .ok_or(LoadingError::BadUrl)
        }
    }

    pub fn lookup_node_by_id(&self, id: &str) -> Option<RsvgNode> {
        self.ids.get(id).map(|n| (*n).clone())
    }

    pub fn lookup_image(&self, href: &str) -> Result<SharedImageSurface, LoadingError> {
        self.images.borrow_mut().lookup(&self.load_options, href)
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        let root = self.root();
        let node_data = root.borrow();

        assert!(node_data.get_type() == NodeType::Svg);
        node_data.get_impl::<NodeSvg>().get_intrinsic_dimensions()
    }
}

struct Resources {
    resources: HashMap<AllowedUrl, Result<Rc<Svg>, LoadingError>>,
}

impl Resources {
    pub fn new() -> Resources {
        Resources {
            resources: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        fragment: &Fragment,
    ) -> Result<RsvgNode, LoadingError> {
        if let Some(ref href) = fragment.uri() {
            self.get_extern_svg(load_options, href).and_then(|svg| {
                svg.lookup_node_by_id(fragment.fragment())
                    .ok_or(LoadingError::BadUrl)
            })
        } else {
            unreachable!();
        }
    }

    fn get_extern_svg(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<Rc<Svg>, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.resources.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let svg = load_svg(load_options, e.key()).map(Rc::new);
                let res = e.insert(svg);
                res.clone()
            }
        }
    }
}

struct Images {
    images: HashMap<AllowedUrl, Result<SharedImageSurface, LoadingError>>,
}

impl Images {
    pub fn new() -> Images {
        Images {
            images: Default::default(),
        }
    }

    pub fn lookup(
        &mut self,
        load_options: &LoadOptions,
        href: &str,
    ) -> Result<SharedImageSurface, LoadingError> {
        let aurl = AllowedUrl::from_href(href, load_options.base_url.as_ref())
            .map_err(|_| LoadingError::BadUrl)?;

        match self.images.entry(aurl) {
            Entry::Occupied(e) => e.get().clone(),
            Entry::Vacant(e) => {
                let surface = load_image(load_options, e.key());
                let res = e.insert(surface);
                res.clone()
            }
        }
    }
}

fn load_svg(load_options: &LoadOptions, aurl: &AllowedUrl) -> Result<Svg, LoadingError> {
    // FIXME: pass a cancellable to these
    io::acquire_stream(aurl, None).and_then(|stream| {
        Svg::load_from_stream(&load_options.copy_with_base_url(aurl), &stream, None)
    })
}

fn load_image(
    load_options: &LoadOptions,
    aurl: &AllowedUrl,
) -> Result<SharedImageSurface, LoadingError> {
    let BinaryData {
        data: bytes,
        content_type,
    } = io::acquire_data(&aurl, None)?;

    if bytes.len() == 0 {
        return Err(LoadingError::EmptyData);
    }

    let loader = if let Some(ref content_type) = content_type {
        PixbufLoader::new_with_mime_type(content_type)?
    } else {
        PixbufLoader::new()
    };

    loader.write(&bytes)?;
    loader.close()?;

    let pixbuf = loader.get_pixbuf().ok_or(LoadingError::Unknown)?;

    let bytes = if load_options.keep_image_data {
        Some(bytes)
    } else {
        None
    };

    let surface =
        SharedImageSurface::from_pixbuf(&pixbuf, bytes, content_type.as_ref().map(String::as_str))?;

    Ok(surface)
}
<|endoftext|>


<commit_before>// Copyright 2012-2016 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.

// Regression test for Issue #30530: alloca's created for storing
// intermediate scratch values during brace-less match arms need to be
// initialized with their drop-flag set to "dropped" (or else we end
// up running the destructors on garbage data at the end of the
// function).

pub enum Handler {
    Default,
    #[allow(dead_code)]
    Custom(*mut Box<dyn Fn()>),
}

fn main() {
    take(Handler::Default, Box::new(main));
}

#[inline(never)]
pub fn take(h: Handler, f: Box<dyn Fn()>) -> Box<dyn Fn()> {
    unsafe {
        match h {
            Handler::Custom(ptr) => *Box::from_raw(ptr),
            Handler::Default => f,
        }
    }
}
<commit_msg>Auto merge of #806 - varkor:patch-1, r=RalfJung<commit_after>// Copyright 2012-2016 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.

// Regression test for Issue #30530: alloca's created for storing
// intermediate scratch values during brace-less match arms need to be
// initialized with their drop-flag set to "dropped" (or else we end
// up running the destructors on garbage data at the end of the
// function).

pub enum Handler {
    Default,
    #[allow(dead_code)]
    Custom(*mut Box<dyn Fn()>),
}

fn main() {
    #[allow(unused_must_use)] {
        take(Handler::Default, Box::new(main));
    }
}

#[inline(never)]
pub fn take(h: Handler, f: Box<dyn Fn()>) -> Box<dyn Fn()> {
    unsafe {
        match h {
            Handler::Custom(ptr) => *Box::from_raw(ptr),
            Handler::Default => f,
        }
    }
}
<|endoftext|>


<commit_before>mod rust_source;

use rust_source::rust_source::{a, d};

fn test_uper_c_source_a()
{
    let mut encoded = [0; 42];
    let mut a: a::A = Default::default();

    // Encode.
    a.a = -1;
    a.b = -2;
    a.c = -3;
    a.d = -4;
    a.e = 1;
    a.f = 2;
    a.g = 3;
    a.h = 4;
    a.i = true;
    a.j.buf = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5];

    assert_eq!(a.to_bytes(&mut encoded).unwrap(), encoded.len());
    assert_eq!(encoded[..],
               [
                   0x7f, 0x7f, 0xfe, 0x7f, 0xff, 0xff, 0xfd, 0x7f, 0xff, 0xff,
                   0xff, 0xff, 0xff, 0xff, 0xfc, 0x01, 0x00, 0x02, 0x00, 0x00,
                   0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
                   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
                   0x82, 0x80
               ][..]);

    // Decode.
    a = Default::default();

    assert_eq!(a.from_bytes(&encoded).unwrap(), encoded.len());
    assert_eq!(a,
               a::A {
                   a: -1,
                   b: -2,
                   c: -3,
                   d: -4,
                   e: 1,
                   f: 2,
                   g: 3,
                   h: 4,
                   i: true,
                   j: a::AJ {
                       buf: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
                   }
               });
}

fn test_uper_c_source_d_all_present()
{
    let mut encoded = [0; 10];
    let mut d: d::D = Default::default();

    // Encode.
    d.length = 1;
    d.elements[0].a.b = d::DElemAB::C(0);
    d.elements[0].a.e.length = 3;
    d.elements[0].g.h = d::DElemGH::J;
    d.elements[0].g.l.length = 2;
    d.elements[0].g.l.buf = [0x54, 0x55];
    d.elements[0].m.is_n_present = true;
    d.elements[0].m.n = false;
    d.elements[0].m.o = 2;
    d.elements[0].m.is_p_present = true;
    d.elements[0].m.p = d::DElemMP {
        q: d::DElemMPQ {
            buf: [3, 3, 3, 3, 3]
        },
        is_r_present: true,
        r: true
    };

    assert_eq!(d.to_bytes(&mut encoded).unwrap(), encoded.len());
    assert_eq!(encoded[..],
               [
                   0x00, 0xd5, 0x15, 0x7a, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0
               ][..]);

    // Decode.
    d = Default::default();

    assert_eq!(d.from_bytes(&encoded).unwrap(), encoded.len());

    assert_eq!(d.length, 1);
    assert_eq!(d.elements[0].a.b, d::DElemAB::C(0));
    assert_eq!(d.elements[0].a.e.length, 3);
    assert_eq!(d.elements[0].g.h, d::DElemGH::J);
    assert_eq!(d.elements[0].g.l.length, 2);
    assert_eq!(d.elements[0].g.l.buf, [0x54, 0x55]);
    assert_eq!(d.elements[0].m.is_n_present, true);
    assert_eq!(d.elements[0].m.n, false);
    assert_eq!(d.elements[0].m.o, 2);
    assert_eq!(d.elements[0].m.is_p_present, true);
    assert_eq!(d.elements[0].m.p, d::DElemMP {
        q: d::DElemMPQ {
            buf: [3, 3, 3, 3, 3]
        },
        is_r_present: true,
        r: true
    });
}

fn main()
{
    test_uper_c_source_a();
    test_uper_c_source_d_all_present();
}
<commit_msg>Opening braces always go on the same line.<commit_after>mod rust_source;

use rust_source::rust_source::{a, d};

fn test_uper_c_source_a() {
    let mut encoded = [0; 42];
    let mut a: a::A = Default::default();

    // Encode.
    a.a = -1;
    a.b = -2;
    a.c = -3;
    a.d = -4;
    a.e = 1;
    a.f = 2;
    a.g = 3;
    a.h = 4;
    a.i = true;
    a.j.buf = [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5];

    assert_eq!(a.to_bytes(&mut encoded).unwrap(), encoded.len());
    assert_eq!(encoded[..],
               [
                   0x7f, 0x7f, 0xfe, 0x7f, 0xff, 0xff, 0xfd, 0x7f, 0xff, 0xff,
                   0xff, 0xff, 0xff, 0xff, 0xfc, 0x01, 0x00, 0x02, 0x00, 0x00,
                   0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
                   0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82,
                   0x82, 0x80
               ][..]);

    // Decode.
    a = Default::default();

    assert_eq!(a.from_bytes(&encoded).unwrap(), encoded.len());
    assert_eq!(a,
               a::A {
                   a: -1,
                   b: -2,
                   c: -3,
                   d: -4,
                   e: 1,
                   f: 2,
                   g: 3,
                   h: 4,
                   i: true,
                   j: a::AJ {
                       buf: [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
                   }
               });
}

fn test_uper_c_source_d_all_present() {
    let mut encoded = [0; 10];
    let mut d: d::D = Default::default();

    // Encode.
    d.length = 1;
    d.elements[0].a.b = d::DElemAB::C(0);
    d.elements[0].a.e.length = 3;
    d.elements[0].g.h = d::DElemGH::J;
    d.elements[0].g.l.length = 2;
    d.elements[0].g.l.buf = [0x54, 0x55];
    d.elements[0].m.is_n_present = true;
    d.elements[0].m.n = false;
    d.elements[0].m.o = 2;
    d.elements[0].m.is_p_present = true;
    d.elements[0].m.p = d::DElemMP {
        q: d::DElemMPQ {
            buf: [3, 3, 3, 3, 3]
        },
        is_r_present: true,
        r: true
    };

    assert_eq!(d.to_bytes(&mut encoded).unwrap(), encoded.len());
    assert_eq!(encoded[..],
               [
                   0x00, 0xd5, 0x15, 0x7a, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0
               ][..]);

    // Decode.
    d = Default::default();

    assert_eq!(d.from_bytes(&encoded).unwrap(), encoded.len());

    assert_eq!(d.length, 1);
    assert_eq!(d.elements[0].a.b, d::DElemAB::C(0));
    assert_eq!(d.elements[0].a.e.length, 3);
    assert_eq!(d.elements[0].g.h, d::DElemGH::J);
    assert_eq!(d.elements[0].g.l.length, 2);
    assert_eq!(d.elements[0].g.l.buf, [0x54, 0x55]);
    assert_eq!(d.elements[0].m.is_n_present, true);
    assert_eq!(d.elements[0].m.n, false);
    assert_eq!(d.elements[0].m.o, 2);
    assert_eq!(d.elements[0].m.is_p_present, true);
    assert_eq!(d.elements[0].m.p, d::DElemMP {
        q: d::DElemMPQ {
            buf: [3, 3, 3, 3, 3]
        },
        is_r_present: true,
        r: true
    });
}

fn main() {
    test_uper_c_source_a();
    test_uper_c_source_d_all_present();
}
<|endoftext|>


<commit_before>//
// rslex - a lexer generator for rust
//
// regexp.rs
// Regexp parsing and representation
//
// Andrei de A. Formiga, 2013-08-09
//

extern mod std;

use buffer::LookaheadBuffer;
//use parser::skip_whitespace;

#[deriving(Eq)]
enum Token { LBrack, RBrack, Id(~str), LParen, RParen, Asterisk,
             Plus, Bar, Dash, String(~str), Eof }

enum Ast { Symb(~str), Str(~str), Union(~Ast, ~Ast),
           Conc(~Ast, ~Ast), Star(~Ast), OnePlus(~Ast),
           CharClass(char, char), Epsilon }

fn parse_string(buffer: &mut LookaheadBuffer, delim: char) -> ~str {
    let mut res : ~str = ~"";
    loop {
        match buffer.next_char() {
            None => fail!("Unexpected end of file. Expected closing {}", delim),
            Some(c) if c == delim => break,
            Some(c) => res.push_char(c)
        }
    }
    res
}

#[test]
fn test_parse_string() {
    let mut b1 = LookaheadBuffer::new("abc'* ");
    assert_eq!(parse_string(&mut b1, '\''), ~"abc");
    assert_eq!(b1.next_char(), Some('*'));

    let mut b2 = LookaheadBuffer::new("abc'def\"  ");
    assert_eq!(parse_string(&mut b2, '"'), ~"abc'def");
    assert_eq!(b2.next_char(), Some(' '));
}

#[test]
#[should_fail]
fn unclosed_string() {
    let mut b1 = LookaheadBuffer::new("abc'def  ");
    assert_eq!(parse_string(&mut b1, '"'), ~"abc'def");
}

#[inline]
fn is_id_char(c: char) -> bool {
    std::char::is_alphanumeric(c) || c == '_'
}

fn parse_id(buffer: &mut LookaheadBuffer, first: char) -> ~str {
    let mut res : ~str = ~"";
    res.push_char(first);
    loop {
        match buffer.next_char() {
            Some(c) if is_id_char(c) => res.push_char(c),
            Some(c) => { buffer.return_char(c); break }
            None => break
        }
    }
    res
}

#[test]
fn test_parse_id() {
    let mut b1 = LookaheadBuffer::new("abc'* ");
    assert_eq!(parse_id(&mut b1, 'x'), ~"xabc");
    assert_eq!(b1.next_char(), Some('\''));

    let mut b2 = LookaheadBuffer::new("bc_def   ");
    assert_eq!(parse_id(&mut b2, 'a'), ~"abc_def");

    let mut b3 = LookaheadBuffer::new("_times|'xy')*   ");
    assert_eq!(parse_id(&mut b3, 'n'), ~"n_times");
    assert_eq!(b3.next_char(), Some('|'));

    let mut b4 = LookaheadBuffer::new(" +xy)*   ");
    assert_eq!(parse_id(&mut b4, 'n'), ~"n");
    assert_eq!(b4.next_char(), Some(' '));
    assert_eq!(b4.next_char(), Some('+'));
    assert_eq!(b4.next_char(), Some('x'));
    assert_eq!(parse_id(&mut b4, 'x'), ~"xy");
}

#[inline]
fn match_next_token(buffer: &mut LookaheadBuffer, t: Token) {
    let rt = get_next_token(buffer);
    if rt != t {
        fail!("Unexpeced token: expected {:?}, got {:?}", t, rt);
    }
}

fn get_next_token(buffer: &mut LookaheadBuffer) -> Token {
    buffer.skip_whitespace();
    match buffer.next_char() {
        Some('[') => LBrack,
        Some(']') => RBrack,
        Some('(') => LParen,
        Some(')') => RParen,
        Some('*') => Asterisk,
        Some('+') => Plus,
        Some('|') => Bar,
        Some('-') => Dash,
        Some('\'') => String(parse_string(buffer, '\'')),
        Some('"') => String(parse_string(buffer, '"')),
        Some(c) if std::char::is_alphabetic(c) => Id(parse_id(buffer, c)),
        None => Eof,
        Some(c) => fail!("Unexpected character: {}", c)
    }
}

#[test]
fn test_get_next_token() {
    let mut b1 = LookaheadBuffer::new("'return'");
    assert_eq!(get_next_token(&mut b1), String(~"return"));

    let mut b2 = LookaheadBuffer::new("return");
    assert_eq!(get_next_token(&mut b2), Id(~"return"));

    let mut b3 = LookaheadBuffer::new("(['a'-'z'])(['A'-'Z'])*");
    assert_eq!(get_next_token(&mut b3), LParen);
    assert_eq!(get_next_token(&mut b3), LBrack);
    assert_eq!(get_next_token(&mut b3), String(~"a"));
    assert_eq!(get_next_token(&mut b3), Dash);
    assert_eq!(get_next_token(&mut b3), String(~"z"));
    assert_eq!(get_next_token(&mut b3), RBrack);
    assert_eq!(get_next_token(&mut b3), RParen);
    assert_eq!(get_next_token(&mut b3), LParen);
    assert_eq!(get_next_token(&mut b3), LBrack);
    assert_eq!(get_next_token(&mut b3), String(~"A"));
    assert_eq!(get_next_token(&mut b3), Dash);
    assert_eq!(get_next_token(&mut b3), String(~"Z"));
    assert_eq!(get_next_token(&mut b3), RBrack);
    assert_eq!(get_next_token(&mut b3), RParen);
    assert_eq!(get_next_token(&mut b3), Asterisk);

    let mut b4 = LookaheadBuffer::new("letter (letter | digit)*");
    assert_eq!(get_next_token(&mut b4), Id(~"letter"));
    assert_eq!(get_next_token(&mut b4), LParen);
    assert_eq!(get_next_token(&mut b4), Id(~"letter"));
    assert_eq!(get_next_token(&mut b4), Bar);
    assert_eq!(get_next_token(&mut b4), Id(~"digit"));
    assert_eq!(get_next_token(&mut b4), RParen);
    assert_eq!(get_next_token(&mut b4), Asterisk);
}

// regexp := union
// union  := union '|' concat | concat
// concat := concat factor | factor
// factor := (regexp) | regexp'*' | regexp'+' | class | id | str
// class  := '[' (char | range)* ']'
// range  := char'-'char

// parse a regexp from buffer until one of the terminators in term occurs
pub fn parse_regexp(buffer: &mut LookaheadBuffer, term: &[~str]) -> Ast {
    parse_union(buffer, term)
}

fn parse_union(buffer: &mut LookaheadBuffer, term: &[~str]) -> Ast {
    let left = parse_concat(buffer, term);
    if get_next_token(buffer) == Bar {
        let right = parse_union(buffer, term);
        Union(~left, ~right)
    }
    else {
        left
    }
}

fn parse_concat(buffer: &mut LookaheadBuffer, term: &[~str]) -> Ast {
    let left = parse_factor(buffer, term);
    let right = parse_factor(buffer, term);
    Conc(~left, ~right)
}

fn trailing_closure(buffer: &mut LookaheadBuffer) -> Option<char> {
    buffer.skip_whitespace();
    match buffer.next_char() {
        Some('*') => Some('*'),
        Some('+') => Some('+'),
        Some(c) => { buffer.return_char(c); None },
        None => None
    }
}

fn parse_character_class(buffer: &mut LookaheadBuffer) -> Ast {
    Epsilon         // TODO
}

fn parse_factor(buffer: &mut LookaheadBuffer, term: &[~str]) -> Ast {
    let pre = match get_next_token(buffer) {
        LParen => { let e = parse_regexp(buffer, term);
                    match_next_token(buffer, RParen);
                    e }
        LBrack => parse_character_class(buffer),
        Id(s) => Symb(s),
        String(s) => Str(s),  // TODO: check for END
        _ => Epsilon          // TODO: error
    };
    match trailing_closure(buffer) {
        Some('*') => Star(~pre),
        Some('+') => OnePlus(~pre),
        Some(_) => fail!("Unexpected closure character"),
        None => pre
    }
}
<commit_msg>Created TokenStream struct for lookahead<commit_after>//
// rslex - a lexer generator for rust
//
// regexp.rs
// Regexp parsing and representation
//
// Andrei de A. Formiga, 2013-08-09
//

extern mod std;

use buffer::LookaheadBuffer;

#[deriving(Eq, Clone)]
enum Token { LBrack, RBrack, Id(~str), LParen, RParen, Asterisk,
             Plus, Bar, Dash, String(~str), End, Eof, Error(char) }

pub enum Ast { Symb(~str), Str(~str), Union(~Ast, ~Ast),
           Conc(~Ast, ~Ast), Star(~Ast), OnePlus(~Ast),
           CharClass(char, char), Epsilon }

/// A token stream with capacity for lookahead of 1 token
struct TokenStream<'r> {
    buffer: &'r mut LookaheadBuffer<'r>,
    term: &'r [char],
    peek: Option<Token>
}

impl<'r> TokenStream<'r> {
    pub fn new(buffer: &'r mut LookaheadBuffer<'r>, term: &'r [char]) -> TokenStream<'r> {
        TokenStream { buffer: buffer, term: term, peek: None }
    }

    fn next_token(&mut self) -> Token {
        let res = match self.peek {
            None => self.next_token_raw(),
            Some(ref t) => (*t).clone()
        };
        self.peek = None;
        res
    }

    fn next_token_raw(&mut self) -> Token {
        self.buffer.skip_whitespace();
        match self.buffer.next_char() {
            Some('[') => LBrack,
            Some(']') => RBrack,
            Some('(') => LParen,
            Some(')') => RParen,
            Some('*') => Asterisk,
            Some('+') => Plus,
            Some('|') => Bar,
            Some('-') => Dash,
            Some('\'') => String(self.parse_string('\'')),
            Some('"') => String(self.parse_string('"')),
            Some(c) if std::char::is_alphabetic(c) => Id(self.parse_id(c)),
            Some(c) if is_terminator(c, self.term) => End,
            None => Eof,
            Some(c) => Error(c)
        }
    }

    fn return_token(&mut self, tok: Token) {
        self.peek = Some(tok);
    }

    fn parse_string(&mut self, delim: char) -> ~str {
        let mut res : ~str = ~"";
        loop {
            match self.buffer.next_char() {
                None => fail!("Unexpected end of file. Expected closing {}", delim),
                Some(c) if c == delim => break,
                Some(c) => res.push_char(c)
            }
        }
        res
    }

    fn parse_id(&mut self, first: char) -> ~str {
        let mut res : ~str = ~"";
        res.push_char(first);
        loop {
            match self.buffer.next_char() {
                Some(c) if is_id_char(c) => res.push_char(c),
                Some(c) => { self.buffer.return_char(c); break }
                None => break
            }
        }
        res
    }

}

#[inline]
fn is_id_char(c: char) -> bool {
    std::char::is_alphanumeric(c) || c == '_'
}

#[inline]
fn match_next_token(ts: &mut TokenStream, t: Token, term: &[char]) {
    let rt = ts.next_token();
    if rt != t {
        fail!("Unexpeced token: expected {:?}, got {:?}", t, rt);
    }
}

#[inline]
fn is_terminator(c: char, term: &[char]) -> bool {
    term.contains(&c)
}

// regexp := union
// union  := union '|' concat | concat
// concat := concat factor | factor
// factor := (regexp) | regexp'*' | regexp'+' | class | id | str
// class  := '[' (char | range)* ']'
// range  := char'-'char

// parse a regexp from the token stream until one of the terminators in term occurs
pub fn parse_regexp(ts: &mut TokenStream, term: &[char]) -> Ast {
    parse_union(ts, term)
}

// fn parse_unconc(buffer: &mut LookaheadBuffer, term: &[char]) -> Ast {
//     Epsilon
// }

fn parse_union(ts: &mut TokenStream, term: &[char]) -> Ast {
    let left = parse_concat(ts, term);
    if ts.next_token() == Bar {
        let right = parse_union(ts, term);
        Union(~left, ~right)
    }
    else {
        left
    }
}

fn parse_concat(ts: &mut TokenStream, term: &[char]) -> Ast {
    let first = parse_factor(ts, term);
    let right = parse_factor(ts, term);
    Conc(~first, ~right)
}

fn trailing_closure(ts: &mut TokenStream) -> Option<Token> {
    match ts.next_token() {
        Asterisk => Some(Asterisk),
        Plus => Some(Plus),
        t => { ts.return_token(t); None }
    }
}

fn parse_character_class(ts: &mut TokenStream) -> Ast {
    Epsilon         // TODO
}

#[inline]
fn parse_factor(ts: &mut TokenStream, term: &[char]) -> Ast {
    let tok = ts.next_token();
    parse_factor_token(ts, tok, term)
}

fn parse_factor_token(ts: &mut TokenStream, tok: Token, term: &[char]) -> Ast {
    let pre = match tok {
        LParen => { let e = parse_regexp(ts, term);
                    match_next_token(ts, RParen, term);
                    e }
        LBrack => parse_character_class(ts),
        Id(s) => Symb(s),
        String(s) => Str(s),  // TODO: check for END
        _ => Epsilon          // TODO: error
    };
    match trailing_closure(ts) {
        Some(Asterisk) => Star(~pre),
        Some(Plus) => OnePlus(~pre),
        Some(_) => fail!("Unexpected closure character"),
        None => pre
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::{LBrack, RBrack, Id, LParen, RParen, Asterisk, Bar, Dash, String, End, Eof, Error };
    use buffer::LookaheadBuffer;

    #[test]
    fn test_parse_string_ts() {
        let term = [','];
        let mut b1 = LookaheadBuffer::new("abc'* ");
        let mut ts1 = TokenStream::new(&mut b1, term);
        assert_eq!(ts1.parse_string('\''), ~"abc");
        assert_eq!(ts1.buffer.next_char(), Some('*'));

        let mut b2 = LookaheadBuffer::new("abc'def\"  ");
        let mut ts2 = TokenStream::new(&mut b2, term);
        assert_eq!(ts2.parse_string('"'), ~"abc'def");
        assert_eq!(ts2.buffer.next_char(), Some(' '));
    }

    #[test]
    #[should_fail]
    fn unclosed_string_ts() {
        let term = [','];
        let mut b1 = LookaheadBuffer::new("abc'def  ");
        let mut ts1 = TokenStream::new(&mut b1, term);
        assert_eq!(ts1.parse_string('"'), ~"abc'def");
    }

    #[test]
    fn test_parse_id_ts() {
        let term = [','];
        let mut b1 = LookaheadBuffer::new("abc'* ");
        let mut ts1 = TokenStream::new(&mut b1, term);
        assert_eq!(ts1.parse_id('x'), ~"xabc");
        assert_eq!(ts1.buffer.next_char(), Some('\''));

        let mut b2 = LookaheadBuffer::new("bc_def   ");
        let mut ts2 = TokenStream::new(&mut b2, term);
        assert_eq!(ts2.parse_id('a'), ~"abc_def");

        let mut b3 = LookaheadBuffer::new("_times|'xy')*   ");
        let mut ts3 = TokenStream::new(&mut b3, term);
        assert_eq!(ts3.parse_id('n'), ~"n_times");
        assert_eq!(ts3.buffer.next_char(), Some('|'));

        let mut b4 = LookaheadBuffer::new(" +xy)*   ");
        let mut ts4 = TokenStream::new(&mut b4, term);
        assert_eq!(ts4.parse_id('n'), ~"n");
        assert_eq!(ts4.buffer.next_char(), Some(' '));
        assert_eq!(ts4.buffer.next_char(), Some('+'));
        assert_eq!(ts4.buffer.next_char(), Some('x'));
        assert_eq!(ts4.parse_id('x'), ~"xy");
    }

    #[test]
    fn test_next_token_raw() {
        let term = [','];
        let mut b1 = LookaheadBuffer::new("'return'");
        let mut ts1 = TokenStream::new(&mut b1, term);
        assert_eq!(ts1.next_token(), String(~"return"));

        let mut b2 = LookaheadBuffer::new("return");
        let mut ts2 = TokenStream::new(&mut b2, term);
        assert_eq!(ts2.next_token(), Id(~"return"));
        ts2.return_token(Id(~"return"));
        assert_eq!(ts2.next_token(), Id(~"return"));
        assert_eq!(ts2.next_token(), Eof);

        let mut b3 = LookaheadBuffer::new("(['a'-'z'])(['A'-'Z'])*");
        let mut ts3 = TokenStream::new(&mut b3, term);
        assert_eq!(ts3.next_token(), LParen);
        assert_eq!(ts3.next_token(), LBrack);
        assert_eq!(ts3.next_token(), String(~"a"));
        assert_eq!(ts3.next_token(), Dash);
        assert_eq!(ts3.next_token(), String(~"z"));
        assert_eq!(ts3.next_token(), RBrack);
        assert_eq!(ts3.next_token(), RParen);
        assert_eq!(ts3.next_token(), LParen);

        assert_eq!(ts3.peek, None);
        ts3.return_token(LParen);
        assert!(!ts3.peek.is_none());
        assert_eq!(ts3.next_token(), LParen);
        assert_eq!(ts3.peek, None);

        assert_eq!(ts3.next_token(), LBrack);
        assert_eq!(ts3.next_token(), String(~"A"));
        assert_eq!(ts3.next_token(), Dash);
        assert_eq!(ts3.next_token(), String(~"Z"));
        assert_eq!(ts3.next_token(), RBrack);
        assert_eq!(ts3.next_token(), RParen);
        assert_eq!(ts3.next_token(), Asterisk);
        assert_eq!(ts3.next_token(), Eof);

        let mut b4 = LookaheadBuffer::new("letter \t (letter | digit)*,");
        let mut ts4 = TokenStream::new(&mut b4, term);
        assert_eq!(ts4.next_token(), Id(~"letter"));
        assert_eq!(ts4.next_token(), LParen);
        assert_eq!(ts4.next_token(), Id(~"letter"));
        assert_eq!(ts4.next_token(), Bar);
        assert_eq!(ts4.next_token(), Id(~"digit"));
        assert_eq!(ts4.next_token(), RParen);
        assert_eq!(ts4.next_token(), Asterisk);
        assert_eq!(ts4.next_token(), End);

        let mut b5 = LookaheadBuffer::new("let  & dig,");
        let mut ts5 = TokenStream::new(&mut b5, term);
        assert_eq!(ts5.next_token(), Id(~"let"));
        assert_eq!(ts5.next_token(), Error('&'));
    }
}<|endoftext|>


<commit_before>extern crate clap;
extern crate regex;

#[cfg(test)]
mod tests {
    include!("../clap-test.rs");
    use clap::{App, Arg, SubCommand, AppSettings, ArgMatches};

    fn get_global_arg() -> Arg<'static, 'static> {
        Arg::with_name("GLOBAL_ARG")
            .long("global-arg")
            .help(
                "Specifies something needed by the subcommands",
            )
            .global(true)
            .takes_value(true)
    }

    fn get_global_flag() -> Arg<'static, 'static> {
        Arg::with_name("GLOBAL_FLAG")
            .long("global-flag")
            .help(
                "Specifies something needed by the subcommands",
            )
            .global(true)
    }

    fn get_app() -> App<'static, 'static> {
        App::new("myprog")
            .global_setting(AppSettings::PropagateGlobalValuesDown)
    }

    fn get_subcommands() -> App<'static, 'static> {
        SubCommand::with_name("outer")
            .subcommand(SubCommand::with_name("inner"))
    }

    fn get_matches(app: App<'static, 'static>, argv: &'static str) -> ArgMatches<'static> {
        app.get_matches_from(argv.split(" ").collect::<Vec<_>>())
    }

    fn get_outer_matches<'a>(m: &'a ArgMatches<'static>) -> &'a ArgMatches<'static> {
        m.subcommand_matches("outer").expect("could not access outer subcommand")
    }

    fn get_inner_matches<'a>(m: &'a ArgMatches<'static>) -> &'a ArgMatches<'static> {
        get_outer_matches(m).subcommand_matches("inner").expect("could not access inner subcommand")
    }

    fn top_can_access_arg<T: Into<Option<&'static str>>>(m: &ArgMatches<'static>, val: T) -> bool {
        m.value_of("GLOBAL_ARG") == val.into()
    }

    fn inner_can_access_arg<T: Into<Option<&'static str>>>(m: &ArgMatches<'static>, val: T) -> bool {
        get_inner_matches(m).value_of("GLOBAL_ARG") == val.into()
    }

    fn outer_can_access_arg<T: Into<Option<&'static str>>>(m: &ArgMatches<'static>, val: T) -> bool {
        get_outer_matches(m).value_of("GLOBAL_ARG") == val.into()
    }

    fn inner_can_access_flag(m: &ArgMatches<'static>, present: bool, occurrences: u64) -> bool {
        let m = get_inner_matches(m);
        (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences)
    }

    fn outer_can_access_flag(m: &ArgMatches<'static>, present: bool, occurrences: u64) -> bool {
        let m = get_outer_matches(m);
        (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences)
    }

    #[test]
    fn global_arg_defined_top_level_used_top_level() {
        let app = get_app()
            .arg(get_global_arg())
            .subcommand(get_subcommands());

        let m = get_matches(app, "myprog --global-arg=some_value outer inner");

        assert!(top_can_access_arg(&m, "some_value"));
        assert!(inner_can_access_arg(&m, "some_value"));
        assert!(outer_can_access_arg(&m, "some_value"));
    }

    #[test]
    fn global_arg_defined_top_level_used_outer() {
        let app = get_app()
            .arg(get_global_arg())
            .subcommand(get_subcommands());

        let m = get_matches(app, "myprog outer --global-arg=some_value inner");

        assert!(top_can_access_arg(&m, "some_value"));
        assert!(inner_can_access_arg(&m, "some_value"));
        assert!(outer_can_access_arg(&m, "some_value"));
    }

    #[test]
    fn global_arg_defined_top_level_used_inner() {
        let app = get_app()
            .arg(get_global_arg())
            .subcommand(get_subcommands());

        let m = get_matches(app, "myprog outer inner --global-arg=some_value");

        assert!(top_can_access_arg(&m, "some_value"), "{:?}", m);
        assert!(inner_can_access_arg(&m, "some_value"));
        assert!(outer_can_access_arg(&m, "some_value"));
    }

    // #[test]
    // fn global_arg_defined_nested_used_top_level() {
    //     let app = get_app()
    //         .subcommand(get_subcommands()
    //             .arg(get_global_arg()));

    //     let m = get_matches(app, "myprog --global-arg=some_value outer inner");

    //     assert!(top_can_access_arg(&m, "some_value"));
    //     assert!(inner_can_access_arg(&m, "some_value"));
    //     assert!(outer_can_access_arg(&m, "some_value"));
    // }

    // #[test]
    // fn global_arg_defined_nested_used_outer() {
    //     let app = get_app()
    //         .subcommand(get_subcommands()
    //             .arg(get_global_arg()));

    //     let m = get_matches(app, "myprog outer --global-arg=some_value inner");

    //     assert!(top_can_access_arg(&m, "some_value"));
    //     assert!(inner_can_access_arg(&m, "some_value"));
    //     assert!(outer_can_access_arg(&m, "some_value"));
    // }

    // #[test]
    // fn global_arg_defined_nested_used_inner() {
    //     let app = get_app()
    //         .subcommand(get_subcommands()
    //             .arg(get_global_arg()));

    //     let m = get_matches(app, "myprog outer inner --global-arg=some_value");

    //     assert!(top_can_access_arg(&m, "some_value"));
    //     assert!(inner_can_access_arg(&m, "some_value"));
    //     assert!(outer_can_access_arg(&m, "some_value"));
    // }
}
<commit_msg>tests: refactors the tests for propagating global values and flags as well as adds some tests for default values and flags<commit_after>extern crate clap;
extern crate regex;

#[cfg(test)]
mod tests {
    include!("../clap-test.rs");
    use clap::{App, Arg, SubCommand, ArgMatches};

    fn get_app() -> App<'static, 'static> {
        App::new("myprog")
            .arg(Arg::with_name("GLOBAL_ARG")
                .long("global-arg")
                .help(
                    "Specifies something needed by the subcommands",
                )
                .global(true)
                .takes_value(true)
                .default_value("default_value"))
            .arg(Arg::with_name("GLOBAL_FLAG")
                .long("global-flag")
                .help(
                    "Specifies something needed by the subcommands",
                )
                .multiple(true)
                .global(true))
            .subcommand(SubCommand::with_name("outer")
                .subcommand(SubCommand::with_name("inner")))
    }

    fn get_matches(app: App<'static, 'static>, argv: &'static str) -> ArgMatches<'static> {
        app.get_matches_from(argv.split(" ").collect::<Vec<_>>())
    }

    fn get_outer_matches<'a>(m: &'a ArgMatches<'static>) -> &'a ArgMatches<'static> {
        m.subcommand_matches("outer").expect("could not access outer subcommand")
    }

    fn get_inner_matches<'a>(m: &'a ArgMatches<'static>) -> &'a ArgMatches<'static> {
        get_outer_matches(m).subcommand_matches("inner").expect("could not access inner subcommand")
    }

    fn top_can_access_arg<T: Into<Option<&'static str>>>(m: &ArgMatches<'static>, val: T) -> bool {
        m.value_of("GLOBAL_ARG") == val.into()
    }

    fn inner_can_access_arg<T: Into<Option<&'static str>>>(m: &ArgMatches<'static>, val: T) -> bool {
        get_inner_matches(m).value_of("GLOBAL_ARG") == val.into()
    }

    fn outer_can_access_arg<T: Into<Option<&'static str>>>(m: &ArgMatches<'static>, val: T) -> bool {
        get_outer_matches(m).value_of("GLOBAL_ARG") == val.into()
    }

    fn top_can_access_flag(m: &ArgMatches<'static>, present: bool, occurrences: u64) -> bool {
        (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences)
    }

    fn inner_can_access_flag(m: &ArgMatches<'static>, present: bool, occurrences: u64) -> bool {
        let m = get_inner_matches(m);
        (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences)
    }

    fn outer_can_access_flag(m: &ArgMatches<'static>, present: bool, occurrences: u64) -> bool {
        let m = get_outer_matches(m);
        (m.is_present("GLOBAL_FLAG") == present) && (m.occurrences_of("GLOBAL_FLAG") == occurrences)
    }

    #[test]
    fn global_arg_used_top_level() {
        let m = get_matches(get_app(), "myprog --global-arg=some_value outer inner");

        assert!(top_can_access_arg(&m, "some_value"));
        assert!(inner_can_access_arg(&m, "some_value"));
        assert!(outer_can_access_arg(&m, "some_value"));
    }

    #[test]
    fn global_arg_used_outer() {
        let m = get_matches(get_app(), "myprog outer --global-arg=some_value inner");

        assert!(top_can_access_arg(&m, "some_value"));
        assert!(inner_can_access_arg(&m, "some_value"));
        assert!(outer_can_access_arg(&m, "some_value"));
    }

    #[test]
    fn global_arg_used_inner() {
        let m = get_matches(get_app(), "myprog outer inner --global-arg=some_value");

        assert!(top_can_access_arg(&m, "some_value"));
        assert!(inner_can_access_arg(&m, "some_value"));
        assert!(outer_can_access_arg(&m, "some_value"));
    }

    #[test]
    fn global_arg_default_value() {
        let m = get_matches(get_app(), "myprog outer inner");

        assert!(top_can_access_arg(&m, "default_value"));
        assert!(inner_can_access_arg(&m, "default_value"));
        assert!(outer_can_access_arg(&m, "default_value"));
    }

    #[test]
    fn global_flag_used_top_level() {
        let m = get_matches(get_app(), "myprog --global-flag outer inner");

        assert!(top_can_access_flag(&m, true, 1));
        assert!(inner_can_access_flag(&m, true, 1));
        assert!(outer_can_access_flag(&m, true, 1));
    }

    #[test]
    fn global_flag_used_outer() {
        let m = get_matches(get_app(), "myprog outer --global-flag inner");

        assert!(top_can_access_flag(&m, true, 1));
        assert!(inner_can_access_flag(&m, true, 1));
        assert!(outer_can_access_flag(&m, true, 1));
    }

    #[test]
    fn global_flag_used_inner() {
        let m = get_matches(get_app(), "myprog outer inner --global-flag");

        assert!(top_can_access_flag(&m, true, 1));
        assert!(inner_can_access_flag(&m, true, 1));
        assert!(outer_can_access_flag(&m, true, 1));
    }

    #[test]
    fn global_flag_2x_used_top_level() {
        let m = get_matches(get_app(), "myprog --global-flag --global-flag outer inner");

        assert!(top_can_access_flag(&m, true, 2));
        assert!(inner_can_access_flag(&m, true, 2));
        assert!(outer_can_access_flag(&m, true, 2));
    }

    #[test]
    fn global_flag_2x_used_inner() {
        let m = get_matches(get_app(), "myprog outer inner --global-flag --global-flag");

        assert!(top_can_access_flag(&m, true, 2));
        assert!(inner_can_access_flag(&m, true, 2));
        assert!(outer_can_access_flag(&m, true, 2));
    }
}
<|endoftext|>


<commit_before>use std::{
    collections::HashMap,
    env, fs,
    path::{Path, PathBuf},
};

pub use eyre;
use eyre::{bail, Result};
pub use fs_utils;
pub use libcnb;
use libcnb::{
    data::{
        build_plan::{Provide, Require},
        buildpack::BuildpackId,
    },
    libcnb_runtime_build, libcnb_runtime_detect, BuildArgs, DetectArgs, Platform,
};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
pub use toml;
pub use tokio;
pub use tracing;

/// The stack id
///
/// Currently a constants (cause there's only one :) but in the
/// future there may be more e.g. `stencila.stacks.jammy`
const CNB_STACK_ID: &str = "stencila.stacks.focal";

/// Test whether the current CNB platform is Stencila
pub fn platform_dir_is_stencila(platform_dir: &Path) -> bool {
    platform_dir.join("env").join("STENCILA_VERSION").exists()
}

/// Test whether the current CNB platform is Stencila
pub fn platform_is_stencila(platform: &impl Platform) -> bool {
    platform.env().contains_key("STENCILA_VERSION")
}

/// String used by buildpacks to indicate that a tool version
/// was determined from the (highest) version found on the system
pub const SYSTEM_INSTALLED: &str = "<system>";

/// A local trait for buildpacks that extends `libcnb::Buildpack`
///
/// Why? To provide some additional introspection and the ability
/// to compile several buildpacks into a single binary and call
/// their `detect` and `build` methods.
pub trait BuildpackTrait: libcnb::Buildpack {
    /// Get the content of the `buildpack.toml` file
    fn toml() -> &'static str;

    /// Get the buildpack's spec (a.k.a. descriptor) from the `buildpack.toml`
    fn spec() -> Result<BuildpackToml> {
        let toml = Self::toml();
        let spec = toml::from_str::<BuildpackToml>(toml)?;
        Ok(spec)
    }

    /// Get the buildpack's `id` from the `buildpack.toml`
    fn id(&self) -> Result<BuildpackId> {
        let spec = Self::spec()?;
        Ok(spec.buildpack.id)
    }

    /// Ensure the buildpack's runtime directory exists and return its path
    ///
    /// Both the `detect` and `build` methods require that `buildpack.toml`
    /// be available on disk.
    fn ensure_dir(&self) -> Result<String> {
        let id = self.id()?;
        let dir = buildpacks_dir()?.join(id.to_string());
        fs::create_dir_all(&dir)?;

        // Write the `buildpack.toml` to the directory
        let toml_path = dir.join("buildpack.toml");
        fs::write(&toml_path, Self::toml())?;

        Ok(dir.display().to_string())
    }

    /// Test whether any of the files exists in the working directory
    ///
    /// A convenience method for use in `detect`.
    fn any_exist(paths: &[&str]) -> bool {
        for path in paths {
            if PathBuf::from(path).exists() {
                return true;
            }
        }
        false
    }

    /// Test whether a file contains a string
    ///
    /// A convenience method for use in `detect`.
    fn file_contains(file: &str, string: &str) -> bool {
        fs::read_to_string(file)
            .map(|content| content.contains(string))
            .unwrap_or(false)
    }

    /// Parses a `.tool-versions` file (if any)
    ///
    /// A convenience method for use in `detect`. If there is no such file, then
    /// the returned map will be empty.
    ///
    /// Note that each line of `.tool-versions` can have multiple versions, but
    /// that this takes the first.
    fn tool_versions() -> HashMap<String, String> {
        match fs::read_to_string(".tool-versions") {
            Ok(content) => content
                .lines()
                .filter_map(|line| {
                    let mut parts = line.split_whitespace();
                    let name = match parts.next() {
                        Some(name) => name.to_string(),
                        None => return None,
                    };
                    let version = parts.next().unwrap_or("*").to_string();
                    Some((name, version))
                })
                .collect(),
            Err(..) => HashMap::new(),
        }
    }

    /// Generate `Require` (with metadata) and `Provide` objects
    ///
    /// A convenience method for use in `build` method.
    fn require_and_provide(
        name: impl AsRef<str>,
        source: impl AsRef<str>,
        desc: impl AsRef<str>,
    ) -> (Require, Provide) {
        let mut require = Require::new(name.as_ref());
        if !source.as_ref().is_empty() {
            require.metadata.insert(
                "source".to_string(),
                toml::Value::String(source.as_ref().to_string()),
            );
        }
        if !desc.as_ref().is_empty() {
            require.metadata.insert(
                "description".to_string(),
                toml::Value::String(desc.as_ref().to_string()),
            );
        }

        let provide = Provide::new(name.as_ref());

        (require, provide)
    }

    /// Split the `name` of a buildpack plan into two parts
    ///
    /// A convenience method for used in `build`.
    fn split_entry_name(name: &str) -> (String, Vec<String>) {
        let mut parts = name.split(' ');
        (
            parts.next().unwrap_or_default().to_string(),
            parts.map(String::from).collect::<Vec<String>>(),
        )
    }

    /// Run the buildpack's `detect` method
    fn detect_with(&self, platform_dir: &Path, build_plan: &Path) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_detect(
            self,
            DetectArgs {
                platform_dir_path: PathBuf::from(platform_dir),
                build_plan_path: PathBuf::from(build_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `detect` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }

    /// Run the buildpack's `build` method
    fn build_with(
        &self,
        layers_dir: &Path,
        platform_dir: &Path,
        buildpack_plan: &Path,
    ) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_build(
            self,
            BuildArgs {
                layers_dir_path: PathBuf::from(layers_dir),
                platform_dir_path: PathBuf::from(platform_dir),
                buildpack_plan_path: PathBuf::from(buildpack_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `build` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }
}

/// Get the path of the directory where buildpacks stored on the users machine
pub fn buildpacks_dir() -> Result<PathBuf> {
    let user_data_dir = dirs::data_dir()
        .unwrap_or_else(|| env::current_dir().expect("Should always be able to get current dir"));
    let dir = match env::consts::OS {
        "macos" | "windows" => user_data_dir.join("Stencila").join("Buildpacks"),
        _ => user_data_dir.join("stencila").join("buildpacks"),
    };
    Ok(dir)
}

/// Generate a locally unique tag for an image based on a directory path
///
/// Build a container image with a tag `<name>-<hash>` where `<name>` is the
/// name of the directory containing the `Dockerfile` and `<hash>` is the 12-character
/// truncated SHA256 hash of its path (to avoid clashes between directories with the same name).
pub fn tag_for_path(path: &Path) -> String {
    let name = path
        .file_name()
        .map(|name| name.to_string_lossy().to_string())
        .unwrap_or_else(|| "unnamed".to_string());

    let mut hash = hash_utils::str_sha256_hex(&path.display().to_string());
    hash.truncate(12);

    [&name, "-", &hash].concat()
}

// The `libcnb` crate provides similar structs to those below, often with stronger typing,
// but those do not implement `Serialize` or `Clone` and so for our purposes
// it was easier to reimplement them here. This decision should be revisited at some time.

/// A Buildpack Descriptor (`buildpack.toml`)
///
/// Used primarily to read in and display the spec for a buildpack for
/// use in commands such a `stencila buildpacks show <label>`.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpacktoml-toml
#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackToml {
    pub api: String,
    pub buildpack: Buildpack,
    pub stacks: Option<Vec<BuildpackStack>>,
    pub order: Option<Vec<BuildpackGroup>>,
    pub metadata: Option<serde_json::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Buildpack {
    pub id: BuildpackId,
    pub version: String,
    pub name: String,
    pub clear_env: Option<bool>,
    pub homepage: Option<String>,
    pub description: Option<String>,
    pub keywords: Option<Vec<String>>,
    #[serde(rename = "sbom-formats")]
    pub sbom_formats: Option<Vec<String>>,
    pub licenses: Option<Vec<BuildpackLicense>>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackLicense {
    pub r#type: Option<String>,
    pub uri: Option<String>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackStack {
    pub id: String,
    pub mixins: Option<Vec<String>>,
}

#[derive(Debug, Default, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackGroup {
    pub id: String,
    pub version: String,
    pub optional: bool,
}

/// A Build Plan
///
/// Generated by the `detect` method of a buildpack (if it matches against a folder).
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#build-plan-toml
#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlan {
    pub provides: Option<Vec<BuildPlanProvides>>,
    pub requires: Option<Vec<BuildPlanRequires>>,
    pub or: Option<Vec<BuildPlanOr>>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanProvides {
    pub name: String,
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanRequires {
    pub name: String,
    pub metadata: Option<toml::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlanOr {
    pub provides: Option<BuildPlanProvides>,
    pub requires: Option<BuildPlanRequires>,
}

/// A Buildpack Plan
///
/// Passed to the `build` method of each buildpack involved in the build.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpack-plan-toml
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackPlan {
    pub entries: Vec<BuildPlanRequires>,
}

/// Runtime entry point function
///
/// Just sets up logging and calls `libcnb::libcnb_runtime`
/// to do the actual work.
pub fn runtime<B: libcnb::Buildpack>(buildpack: &B) {
    tracing_subscriber::fmt().init();
    libcnb::libcnb_runtime(buildpack);
}

/// Generate a `main` function for a buildpack
#[macro_export]
macro_rules! buildpack_main {
    ($buildpack:ident) => {
        #[tokio::main]
        async fn main() {
            buildpack::runtime(&$buildpack);
        }
    };
}
<commit_msg>chore(Buildpacks): Add `prepend_path` method<commit_after>use std::{
    collections::HashMap,
    env,
    ffi::OsString,
    fs,
    path::{Path, PathBuf},
};

pub use eyre;
use eyre::{bail, Result};
pub use fs_utils;
pub use libcnb;
use libcnb::{
    data::{
        build_plan::{Provide, Require},
        buildpack::BuildpackId,
    },
    libcnb_runtime_build, libcnb_runtime_detect, BuildArgs, DetectArgs, Platform,
};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
pub use tokio;
pub use toml;
pub use tracing;

/// The stack id
///
/// Currently a constants (cause there's only one :) but in the
/// future there may be more e.g. `stencila.stacks.jammy`
const CNB_STACK_ID: &str = "stencila.stacks.focal";

/// Test whether the current CNB platform is Stencila
pub fn platform_dir_is_stencila(platform_dir: &Path) -> bool {
    platform_dir.join("env").join("STENCILA_VERSION").exists()
}

/// Test whether the current CNB platform is Stencila
pub fn platform_is_stencila(platform: &impl Platform) -> bool {
    platform.env().contains_key("STENCILA_VERSION")
}

/// String used by buildpacks to indicate that a tool version
/// was determined from the (highest) version found on the system
pub const SYSTEM_INSTALLED: &str = "<system>";

/// A local trait for buildpacks that extends `libcnb::Buildpack`
///
/// Why? To provide some additional introspection and the ability
/// to compile several buildpacks into a single binary and call
/// their `detect` and `build` methods.
pub trait BuildpackTrait: libcnb::Buildpack {
    /// Get the content of the `buildpack.toml` file
    fn toml() -> &'static str;

    /// Get the buildpack's spec (a.k.a. descriptor) from the `buildpack.toml`
    fn spec() -> Result<BuildpackToml> {
        let toml = Self::toml();
        let spec = toml::from_str::<BuildpackToml>(toml)?;
        Ok(spec)
    }

    /// Get the buildpack's `id` from the `buildpack.toml`
    fn id(&self) -> Result<BuildpackId> {
        let spec = Self::spec()?;
        Ok(spec.buildpack.id)
    }

    /// Ensure the buildpack's runtime directory exists and return its path
    ///
    /// Both the `detect` and `build` methods require that `buildpack.toml`
    /// be available on disk.
    fn ensure_dir(&self) -> Result<String> {
        let id = self.id()?;
        let dir = buildpacks_dir()?.join(id.to_string());
        fs::create_dir_all(&dir)?;

        // Write the `buildpack.toml` to the directory
        let toml_path = dir.join("buildpack.toml");
        fs::write(&toml_path, Self::toml())?;

        Ok(dir.display().to_string())
    }

    /// Test whether any of the files exists in the working directory
    ///
    /// A convenience method for use in `detect`.
    fn any_exist(paths: &[&str]) -> bool {
        for path in paths {
            if PathBuf::from(path).exists() {
                return true;
            }
        }
        false
    }

    /// Test whether a file contains a string
    ///
    /// A convenience method for use in `detect`.
    fn file_contains(file: &str, string: &str) -> bool {
        fs::read_to_string(file)
            .map(|content| content.contains(string))
            .unwrap_or(false)
    }

    /// Prepend a path to the `PATH` env var
    fn prepend_path(first: &Path) -> Result<OsString> {
        if let Some(path) = env::var_os("PATH") {
            let mut paths = vec![first.into()];
            paths.append(&mut env::split_paths(&path).collect::<Vec<_>>());
            Ok(env::join_paths(paths)?)
        } else {
            Ok(first.into())
        }
    }

    /// Parses a `.tool-versions` file (if any)
    ///
    /// A convenience method for use in `detect`. If there is no such file, then
    /// the returned map will be empty.
    ///
    /// Note that each line of `.tool-versions` can have multiple versions, but
    /// that this takes the first.
    fn tool_versions() -> HashMap<String, String> {
        match fs::read_to_string(".tool-versions") {
            Ok(content) => content
                .lines()
                .filter_map(|line| {
                    let mut parts = line.split_whitespace();
                    let name = match parts.next() {
                        Some(name) => name.to_string(),
                        None => return None,
                    };
                    let version = parts.next().unwrap_or("*").to_string();
                    Some((name, version))
                })
                .collect(),
            Err(..) => HashMap::new(),
        }
    }

    /// Generate `Require` (with metadata) and `Provide` objects
    ///
    /// A convenience method for use in `build` method.
    fn require_and_provide(
        name: impl AsRef<str>,
        source: impl AsRef<str>,
        desc: impl AsRef<str>,
    ) -> (Require, Provide) {
        let mut require = Require::new(name.as_ref());
        if !source.as_ref().is_empty() {
            require.metadata.insert(
                "source".to_string(),
                toml::Value::String(source.as_ref().to_string()),
            );
        }
        if !desc.as_ref().is_empty() {
            require.metadata.insert(
                "description".to_string(),
                toml::Value::String(desc.as_ref().to_string()),
            );
        }

        let provide = Provide::new(name.as_ref());

        (require, provide)
    }

    /// Split the `name` of a buildpack plan into two parts
    ///
    /// A convenience method for used in `build`.
    fn split_entry_name(name: &str) -> (String, Vec<String>) {
        let mut parts = name.split(' ');
        (
            parts.next().unwrap_or_default().to_string(),
            parts.map(String::from).collect::<Vec<String>>(),
        )
    }

    /// Run the buildpack's `detect` method
    fn detect_with(&self, platform_dir: &Path, build_plan: &Path) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_detect(
            self,
            DetectArgs {
                platform_dir_path: PathBuf::from(platform_dir),
                build_plan_path: PathBuf::from(build_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `detect` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }

    /// Run the buildpack's `build` method
    fn build_with(
        &self,
        layers_dir: &Path,
        platform_dir: &Path,
        buildpack_plan: &Path,
    ) -> Result<i32>
    where
        Self: Sized,
    {
        env::set_var("CNB_STACK_ID", CNB_STACK_ID);

        let buildpack_dir = self.ensure_dir()?;
        env::set_var("CNB_BUILDPACK_DIR", buildpack_dir);

        match libcnb_runtime_build(
            self,
            BuildArgs {
                layers_dir_path: PathBuf::from(layers_dir),
                platform_dir_path: PathBuf::from(platform_dir),
                buildpack_plan_path: PathBuf::from(buildpack_plan),
            },
        ) {
            Ok(code) => Ok(code),
            Err(error) => bail!(
                "While running `build` for buildpack `{}`: {}",
                self.id()?,
                error
            ),
        }
    }
}

/// Get the path of the directory where buildpacks stored on the users machine
pub fn buildpacks_dir() -> Result<PathBuf> {
    let user_data_dir = dirs::data_dir()
        .unwrap_or_else(|| env::current_dir().expect("Should always be able to get current dir"));
    let dir = match env::consts::OS {
        "macos" | "windows" => user_data_dir.join("Stencila").join("Buildpacks"),
        _ => user_data_dir.join("stencila").join("buildpacks"),
    };
    Ok(dir)
}

/// Generate a locally unique tag for an image based on a directory path
///
/// Build a container image with a tag `<name>-<hash>` where `<name>` is the
/// name of the directory containing the `Dockerfile` and `<hash>` is the 12-character
/// truncated SHA256 hash of its path (to avoid clashes between directories with the same name).
pub fn tag_for_path(path: &Path) -> String {
    let name = path
        .file_name()
        .map(|name| name.to_string_lossy().to_string())
        .unwrap_or_else(|| "unnamed".to_string());

    let mut hash = hash_utils::str_sha256_hex(&path.display().to_string());
    hash.truncate(12);

    [&name, "-", &hash].concat()
}

// The `libcnb` crate provides similar structs to those below, often with stronger typing,
// but those do not implement `Serialize` or `Clone` and so for our purposes
// it was easier to reimplement them here. This decision should be revisited at some time.

/// A Buildpack Descriptor (`buildpack.toml`)
///
/// Used primarily to read in and display the spec for a buildpack for
/// use in commands such a `stencila buildpacks show <label>`.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpacktoml-toml
#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackToml {
    pub api: String,
    pub buildpack: Buildpack,
    pub stacks: Option<Vec<BuildpackStack>>,
    pub order: Option<Vec<BuildpackGroup>>,
    pub metadata: Option<serde_json::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Buildpack {
    pub id: BuildpackId,
    pub version: String,
    pub name: String,
    pub clear_env: Option<bool>,
    pub homepage: Option<String>,
    pub description: Option<String>,
    pub keywords: Option<Vec<String>>,
    #[serde(rename = "sbom-formats")]
    pub sbom_formats: Option<Vec<String>>,
    pub licenses: Option<Vec<BuildpackLicense>>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackLicense {
    pub r#type: Option<String>,
    pub uri: Option<String>,
}

#[skip_serializing_none]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct BuildpackStack {
    pub id: String,
    pub mixins: Option<Vec<String>>,
}

#[derive(Debug, Default, Clone, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackGroup {
    pub id: String,
    pub version: String,
    pub optional: bool,
}

/// A Build Plan
///
/// Generated by the `detect` method of a buildpack (if it matches against a folder).
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#build-plan-toml
#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlan {
    pub provides: Option<Vec<BuildPlanProvides>>,
    pub requires: Option<Vec<BuildPlanRequires>>,
    pub or: Option<Vec<BuildPlanOr>>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanProvides {
    pub name: String,
}

#[skip_serializing_none]
#[derive(Debug, Deserialize, Serialize)]
pub struct BuildPlanRequires {
    pub name: String,
    pub metadata: Option<toml::Value>,
}

#[skip_serializing_none]
#[derive(Debug, Default, Deserialize, Serialize)]
pub struct BuildPlanOr {
    pub provides: Option<BuildPlanProvides>,
    pub requires: Option<BuildPlanRequires>,
}

/// A Buildpack Plan
///
/// Passed to the `build` method of each buildpack involved in the build.
///
/// See https://github.com/buildpacks/spec/blob/main/buildpack.md#buildpack-plan-toml
#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(default)]
pub struct BuildpackPlan {
    pub entries: Vec<BuildPlanRequires>,
}

/// Runtime entry point function
///
/// Just sets up logging and calls `libcnb::libcnb_runtime`
/// to do the actual work.
pub fn runtime<B: libcnb::Buildpack>(buildpack: &B) {
    tracing_subscriber::fmt().init();
    libcnb::libcnb_runtime(buildpack);
}

/// Generate a `main` function for a buildpack
#[macro_export]
macro_rules! buildpack_main {
    ($buildpack:ident) => {
        #[tokio::main]
        async fn main() {
            buildpack::runtime(&$buildpack);
        }
    };
}
<|endoftext|>