<commit_before>use dungeon::{Dungeon};
use celloption::{Tile};
use genotype::{Genotype};
use phenotype::{Seed};
use config::{Config};

use std::iter::{repeat};
use std::collections::{HashMap};
use rand::{Rng};
use util::{odds};

#[derive(Clone, Debug)]
pub struct WallPatterns {
    seed: Seed,
    patterns: Vec<Pattern>,
    pattern_width: u32,
    pattern_height: u32,
    indices: Vec<usize>
}

#[derive(Clone, Debug)]
struct Pattern {
    pattern: Vec<Option<Tile>>
}

impl Pattern {
    fn from_config(mapping: &HashMap<char, &Tile>, description: Vec<String>, width: u32, height: u32) -> Pattern {
        let size = width as usize * height as usize;
        let mut pattern = Vec::with_capacity(size);
        for line in description {
            for ch in line.chars() {
                let tile = mapping.get(&(ch));
                match tile {
                    None => pattern.push(None),
                    Some(&tile) => pattern.push(Some((*tile).clone()))
                }
            }
        }
        assert!(pattern.len() == size); // sanity check
        Pattern {
            pattern: pattern
        }
    }
}

impl WallPatterns {
    pub fn new(config: &Config, seed: &Seed) -> WallPatterns {
        // read in the configurations
        let wallpatterns = config.get_table(None, "wallpatterns");
        let tile_vars = config.get_table(Some(wallpatterns), "tiles");
        let pattern_width = config.get_integer(tile_vars, "width") as u32;
        let pattern_height = config.get_integer(tile_vars, "height") as u32;
        let mut mapping = HashMap::new();
        let tiles = config.get_listing(tile_vars, vec!["width, height"]);
        for tile in tiles {
            let name = tile.as_slice();
            let graphical_tile = seed.tiles.get(name).unwrap();
            mapping.insert(config.get_char(tile_vars, name), graphical_tile);
        }
        let room_vars = config.get_table(Some(wallpatterns), "rooms");
        let rooms = config.get_listing(room_vars, vec![]);
        let patterns: Vec<Pattern> = rooms.iter().map(|r| {
            let description = config.get_array(room_vars, r);
            Pattern::from_config(&mapping, description, pattern_width, pattern_height)
        }).collect();
        WallPatterns {
            seed: seed.clone(),
            patterns: patterns,
            pattern_width: pattern_width,
            pattern_height: pattern_height,
            indices: vec![],
        }
    }

}

impl Genotype for WallPatterns {
    fn initialize<T: Rng>(&self, rng: &mut T) -> WallPatterns {
        assert!(self.patterns.len() != 0);
        let indices = rng.gen_iter::<usize>().take(self.patterns.len()).map(|v| v % self.patterns.len()).collect();
        WallPatterns {
            seed: self.seed.clone(),
            patterns: self.patterns.clone(),
            pattern_width: self.pattern_width,
            pattern_height: self.pattern_height,
            indices: indices,
        }
    }

    fn mutate<T: Rng>(&mut self, rng: &mut T, percentage: f64) {
        let length = self.patterns.len();
        let n = (length as f64 * percentage) as u32;
        for _ in range(0, n) {
            let index = rng.gen_range(1, length);
            self.indices[index] = index;
        }
    }

    fn generate(&self) -> Dungeon {
        // draw the patterns according to the indices we have.
        let w = self.seed.width;
        let h = self.seed.height;
        let n = self.patterns.len();
        let mut dungeon = Dungeon::new(w, h, None);
        for i in 0..w {
            let x: u32 = i / self.pattern_width;
            let inner_x = i % self.pattern_width;
            for j in 0..h {
                let y: u32 = j / self.pattern_height;
                let index = self.indices[(y * self.pattern_width + x) as usize % n];
                let ref pattern = self.patterns[index];
                // here, we have to invert due to a mismatch between how we draw the patterns and opengl coords
                let inner_y = self.pattern_height - (j % self.pattern_height) - 1;
                let tile_index = inner_y * self.pattern_width + inner_x;
                let tile = pattern.pattern[tile_index as usize].clone();
                dungeon.cells[i as usize][j as usize].tile = tile.clone();
            }
        }
        dungeon.clone()
    }
}
<commit_msg>Minor bug fix.<commit_after>use dungeon::{Dungeon};
use celloption::{Tile};
use genotype::{Genotype};
use phenotype::{Seed};
use config::{Config};

use std::iter::{repeat};
use std::collections::{HashMap};
use rand::{Rng};
use util::{odds};

#[derive(Clone, Debug)]
pub struct WallPatterns {
    seed: Seed,
    patterns: Vec<Pattern>,
    pattern_width: u32,
    pattern_height: u32,
    indices: Vec<usize>
}

#[derive(Clone, Debug)]
struct Pattern {
    pattern: Vec<Option<Tile>>
}

impl Pattern {
    fn from_config(mapping: &HashMap<char, &Tile>, description: Vec<String>, width: u32, height: u32) -> Pattern {
        let size = width as usize * height as usize;
        let mut pattern = Vec::with_capacity(size);
        for line in description {
            for ch in line.chars() {
                let tile = mapping.get(&(ch));
                match tile {
                    None => pattern.push(None),
                    Some(&tile) => pattern.push(Some((*tile).clone()))
                }
            }
        }
        assert!(pattern.len() == size); // sanity check
        Pattern {
            pattern: pattern
        }
    }
}

impl WallPatterns {
    pub fn new(config: &Config, seed: &Seed) -> WallPatterns {
        // read in the configurations
        let wallpatterns = config.get_table(None, "wallpatterns");
        let tile_vars = config.get_table(Some(wallpatterns), "tiles");
        let pattern_width = config.get_integer(tile_vars, "width") as u32;
        let pattern_height = config.get_integer(tile_vars, "height") as u32;
        let mut mapping = HashMap::new();
        let tiles = config.get_listing(tile_vars, vec!["width", "height"]);
        for tile in tiles {
            let name = tile.as_slice();
            let graphical_tile = seed.tiles.get(name).unwrap();
            mapping.insert(config.get_char(tile_vars, name), graphical_tile);
        }
        let room_vars = config.get_table(Some(wallpatterns), "rooms");
        let rooms = config.get_listing(room_vars, vec![]);
        let patterns: Vec<Pattern> = rooms.iter().map(|r| {
            let description = config.get_array(room_vars, r);
            Pattern::from_config(&mapping, description, pattern_width, pattern_height)
        }).collect();
        WallPatterns {
            seed: seed.clone(),
            patterns: patterns,
            pattern_width: pattern_width,
            pattern_height: pattern_height,
            indices: vec![],
        }
    }

}

impl Genotype for WallPatterns {
    fn initialize<T: Rng>(&self, rng: &mut T) -> WallPatterns {
        assert!(self.patterns.len() != 0);
        let indices = rng.gen_iter::<usize>().take(self.patterns.len()).map(|v| v % self.patterns.len()).collect();
        WallPatterns {
            seed: self.seed.clone(),
            patterns: self.patterns.clone(),
            pattern_width: self.pattern_width,
            pattern_height: self.pattern_height,
            indices: indices,
        }
    }

    fn mutate<T: Rng>(&mut self, rng: &mut T, percentage: f64) {
        let length = self.patterns.len();
        let n = (length as f64 * percentage) as u32;
        for _ in range(0, n) {
            let index = rng.gen_range(1, length);
            self.indices[index] = index;
        }
    }

    fn generate(&self) -> Dungeon {
        // draw the patterns according to the indices we have.
        let w = self.seed.width;
        let h = self.seed.height;
        let n = self.patterns.len();
        let mut dungeon = Dungeon::new(w, h, None);
        for i in 0..w {
            let x: u32 = i / self.pattern_width;
            let inner_x = i % self.pattern_width;
            for j in 0..h {
                let y: u32 = j / self.pattern_height;
                let index = self.indices[(y * self.pattern_width + x) as usize % n];
                let ref pattern = self.patterns[index];
                // here, we have to invert due to a mismatch between how we draw the patterns and opengl coords
                let inner_y = self.pattern_height - (j % self.pattern_height) - 1;
                let tile_index = inner_y * self.pattern_width + inner_x;
                let tile = pattern.pattern[tile_index as usize].clone();
                dungeon.cells[i as usize][j as usize].tile = tile.clone();
            }
        }
        dungeon.clone()
    }
}
<|endoftext|>


<commit_before>use std::{mem};

use deserializer::{DeserializationError, DeserializationResult};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ObjectIdentifier {
    pub parts: Vec<u32>
}

impl ObjectIdentifier {
    pub fn new(oid: Vec<u32>) -> Option<ObjectIdentifier> {
        if oid.len() < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
            return None;
        }

        return Some(ObjectIdentifier{
            parts: oid,
        });
    }
}

fn _int_length(v: i64) -> usize {
    let mut num_bytes = 1;
    let mut i = v;

    while i > 127 || i < -128 {
        num_bytes += 1;
        i >>= 8;
    }
    return num_bytes;
}

pub trait Integer {
    fn encode(&self) -> Vec<u8>;
    fn decode(Vec<u8>) -> DeserializationResult<Self>;
}

macro_rules! integer {
    ($Int:ident) => {
        impl Integer for $Int {
            fn encode(&self) -> Vec<u8> {
                let n = _int_length(*self as i64);
                let mut result = Vec::with_capacity(n);
                for i in (1..n+1).rev() {
                    result.push((self >> ((i - 1) * 8)) as u8);
                }
                return result;
            }

            fn decode(data: Vec<u8>) -> DeserializationResult<$Int> {
                if data.len() > mem::size_of::<$Int>() {
                    return Err(DeserializationError::IntegerOverflow);
                }
                let mut ret = 0;
                for b in data.iter() {
                    ret <<= 8;
                    ret |= *b as i64;
                }
                // Shift up and down in order to sign extend the result.
                ret <<= 64 - data.len() * 8;
                ret >>= 64 - data.len() * 8;
                return Ok(ret as $Int);
            }
        }
    }
}

integer!(i8);
integer!(i32);
integer!(i64);

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

    #[test]
    fn test_object_identifier_new() {
        assert!(ObjectIdentifier::new(vec![]).is_none());
        assert!(ObjectIdentifier::new(vec![3, 10]).is_none());
        assert!(ObjectIdentifier::new(vec![1, 50]).is_none());
    }
}
<commit_msg>give this a better name<commit_after>use std::{mem};

use deserializer::{DeserializationError, DeserializationResult};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ObjectIdentifier {
    pub parts: Vec<u32>
}

impl ObjectIdentifier {
    pub fn new(oid: Vec<u32>) -> Option<ObjectIdentifier> {
        if oid.len() < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
            return None;
        }

        return Some(ObjectIdentifier{
            parts: oid,
        });
    }
}

fn _int_length(v: i64) -> usize {
    let mut num_bytes = 1;
    let mut i = v;

    while i > 127 || i < -128 {
        num_bytes += 1;
        i >>= 8;
    }
    return num_bytes;
}

pub trait Integer {
    fn encode(&self) -> Vec<u8>;
    fn decode(Vec<u8>) -> DeserializationResult<Self>;
}

macro_rules! primitive_integer {
    ($Int:ident) => {
        impl Integer for $Int {
            fn encode(&self) -> Vec<u8> {
                let n = _int_length(*self as i64);
                let mut result = Vec::with_capacity(n);
                for i in (1..n+1).rev() {
                    result.push((self >> ((i - 1) * 8)) as u8);
                }
                return result;
            }

            fn decode(data: Vec<u8>) -> DeserializationResult<$Int> {
                if data.len() > mem::size_of::<$Int>() {
                    return Err(DeserializationError::IntegerOverflow);
                }
                let mut ret = 0;
                for b in data.iter() {
                    ret <<= 8;
                    ret |= *b as i64;
                }
                // Shift up and down in order to sign extend the result.
                ret <<= 64 - data.len() * 8;
                ret >>= 64 - data.len() * 8;
                return Ok(ret as $Int);
            }
        }
    }
}

primitive_integer!(i8);
primitive_integer!(i32);
primitive_integer!(i64);

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

    #[test]
    fn test_object_identifier_new() {
        assert!(ObjectIdentifier::new(vec![]).is_none());
        assert!(ObjectIdentifier::new(vec![3, 10]).is_none());
        assert!(ObjectIdentifier::new(vec![1, 50]).is_none());
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::rc::Rc;
use std::cell::RefCell;
use std::fs::{remove_dir_all, create_dir_all, rename};
use std::fs::{remove_file, remove_dir, PathExt};
use std::io::{stdout, stderr};
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};

use argparse::{ArgumentParser, Store, StoreTrue};
use nix::unistd::close;
use nix::unistd::pipe;
use rustc_serialize::json;

use container::mount::{bind_mount, unmount};
use container::monitor::{Monitor, Executor, MonitorStatus};
use container::monitor::MonitorResult::{Killed, Exit};
use container::container::{Command};
use container::uidmap::{Uidmap, map_users};
use container::vagga::container_ver;
use container::pipe::CPipe;
use config::{Container, Settings};
use config::builders::Builder as B;
use config::builders::Source as S;
use super::super::file_util::create_dir;
use super::Wrapper;
use super::setup;

struct RunBuilder<'a> {
    container: String,
    settings: &'a Settings,
    uid_map: &'a Uidmap,
}

struct RunVersion<'a> {
    container: String,
    pipe: Option<CPipe>,
    result: Rc<RefCell<String>>,
    uid_map: &'a Uidmap,
    settings: &'a Settings,
}

impl<'a> Executor for RunVersion<'a> {
    fn command(&mut self) -> Command {
        let mut cmd = Command::vagga("vagga_version", "/vagga/bin/vagga");
        cmd.keep_sigmask();
        cmd.set_uidmap(self.uid_map.clone());
        cmd.arg(&self.container);
        cmd.arg("--settings");
        cmd.arg(json::encode(self.settings).unwrap());
        cmd.set_env("TERM".to_string(), "dumb".to_string());
        println!("Pipe {} / {}",
            self.pipe.as_ref().unwrap().reader,
            self.pipe.as_ref().unwrap().writer);
        cmd.set_stdout_fd(self.pipe.as_ref().unwrap().writer);
        if let Ok(x) = env::var("RUST_LOG") {
            cmd.set_env("RUST_LOG".to_string(), x);
        }
        if let Ok(x) = env::var("RUST_BACKTRACE") {
            cmd.set_env("RUST_BACKTRACE".to_string(), x);
        }
        return cmd;
    }
    fn finish(&mut self, status: i32) -> MonitorStatus {
        if status == 0 {
            *self.result.borrow_mut() = String::from_utf8(
                // TODO(tailhook) graceful process of few unwraps
                self.pipe.take().unwrap().read().unwrap()).unwrap();
        }
        return MonitorStatus::Shutdown(status)
    }
}

impl<'a> Executor for RunBuilder<'a> {
    fn command(&mut self) -> Command {
        let mut cmd = Command::vagga("vagga_build", "/vagga/bin/vagga");
        cmd.keep_sigmask();
        cmd.set_uidmap(self.uid_map.clone());
        cmd.container();
        cmd.arg(&self.container);
        cmd.arg("--settings");
        cmd.arg(json::encode(self.settings).unwrap());
        cmd.set_env("TERM".to_string(),
                    env::var("TERM").unwrap_or("dumb".to_string()));
        if let Ok(x) = env::var("RUST_LOG") {
            cmd.set_env("RUST_LOG".to_string(), x);
        }
        if let Ok(x) = env::var("RUST_BACKTRACE") {
            cmd.set_env("RUST_BACKTRACE".to_string(), x);
        }
        return cmd;
    }
}

pub fn prepare_tmp_root_dir(path: &Path) -> Result<(), String> {
    if path.exists() {
        try!(remove_dir_all(path)
             .map_err(|x| format!("Error removing directory: {}", x)));
    }
    try_msg!(create_dir(path, true),
         "Error creating directory: {err}");
    let rootdir = path.join("root");
    try_msg!(create_dir(&rootdir, false),
         "Error creating directory: {err}");

    let tgtbase = Path::new("/vagga/container");
    try_msg!(create_dir(&tgtbase, false),
         "Error creating directory: {err}");
    try!(bind_mount(path, &tgtbase));

    let tgtroot = Path::new("/vagga/root");
    try_msg!(create_dir(&tgtroot, false),
         "Error creating directory: {err}");
    try!(bind_mount(&rootdir, &tgtroot));

    try_msg!(create_dir(&tgtroot.join("dev"), false),
         "Error creating directory: {err}");
    try_msg!(create_dir(&tgtroot.join("sys"), false),
         "Error creating directory: {err}");
    try_msg!(create_dir(&tgtroot.join("proc"), false),
         "Error creating directory: {err}");
    try_msg!(create_dir(&tgtroot.join("work"), false),
         "Error creating directory: {err}");
    return Ok(());
}

pub fn commit_root(tmp_path: &Path, final_path: &Path) -> Result<(), String> {
    let mut path_to_remove = None;
    if final_path.exists() {
        let rempath = tmp_path.with_file_name(
            // TODO(tailhook) consider these unwraps
            tmp_path.file_name().unwrap().to_str()
            .unwrap().to_string() + ".old");
        try!(rename(final_path, &rempath)
             .map_err(|x| format!("Error renaming old dir: {}", x)));
        path_to_remove = Some(rempath);
    }
    try!(rename(tmp_path, final_path)
         .map_err(|x| format!("Error renaming dir: {}", x)));
    if let Some(ref path_to_remove) = path_to_remove {
        try!(remove_dir_all(path_to_remove)
             .map_err(|x| format!("Error removing old dir: {}", x)));
    }
    return Ok(());
}

pub fn get_version_hash(container: String, wrapper: &Wrapper)
    -> Result<Option<String>, String>
{
    let cconfig = try!(wrapper.config.containers.get(&container)
        .ok_or(format!("Container {} not found", container)));
    let uid_map = try!(map_users(wrapper.settings,
        &cconfig.uids, &cconfig.gids));
    let mut mon = Monitor::new();
    let ver = Rc::new(RefCell::new("".to_string()));
    mon.add(Rc::new("version".to_string()), Box::new(RunVersion {
        container: container,
        pipe: Some(CPipe::new().ok().expect("Can't create pipe")),
        result: ver.clone(),
        settings: wrapper.settings,
        uid_map: &uid_map,
    }));
    match mon.run() {
        Killed => return Err(format!("Versioner has died")),
        Exit(0) => {},
        Exit(29) => return Ok(None),
        Exit(val) => return Err(format!("Versioner exited with code {}", val)),
    };
    return Ok(Some(ver.borrow().to_string()));
}

pub fn build_container(container: &String, force: bool, wrapper: &Wrapper)
    -> Result<String, String>
{
    let cconfig = try!(wrapper.config.containers.get(container)
        .ok_or(format!("Container {} not found", container)));
    let name = try!(_build_container(cconfig, container, force, wrapper));
    let destlink = Path::new("/work/.vagga").join(&container);
    let tmplink = destlink.with_extension("tmp");
    if tmplink.exists() {
        try!(remove_file(&tmplink)
            .map_err(|e| format!("Error removing temporary link: {}", e)));
    }
    let roots = if wrapper.ext_settings.storage_dir.is_some() {
        Path::new(".lnk/.roots")
    } else {
        Path::new(".roots")
    };
    let linkval = roots.join(&name).join("root");
    if cconfig.auto_clean {
        container_ver(container).map(|oldname| {
            if oldname != name {
                let base = Path::new("/vagga/base/.roots");
                let dir = base.join(&oldname);
                let tmpdir = base.join(".tmp".to_string() + &oldname);
                rename(&dir, &tmpdir)
                    .map_err(|e| error!("Error renaming old dir: {}", e));
                remove_dir_all(&tmpdir)
                    .map_err(|e| error!("Error removing old dir: {}", e));
            }
        });
    }
    try!(symlink(&linkval, &tmplink)
         .map_err(|e| format!("Error symlinking container: {}", e)));
    try!(rename(&tmplink, &destlink)
         .map_err(|e| format!("Error renaming symlink: {}", e)));
    return Ok(name);
}

pub fn _build_container(cconfig: &Container, container: &String,
    force: bool, wrapper: &Wrapper)
    -> Result<String, String>
{
    let uid_map = try!(map_users(wrapper.settings,
        &cconfig.uids, &cconfig.gids));
    let mut mon = Monitor::new();
    let ver = Rc::new(RefCell::new("".to_string()));
    mon.add(Rc::new("version".to_string()), Box::new(RunVersion {
        container: container.clone(),
        pipe: Some(CPipe::new().ok().expect("Can't create pipe")),
        result: ver.clone(),
        settings: wrapper.settings,
        uid_map: &uid_map,
    }));
    match mon.run() {
        Killed => return Err(format!("Builder has died")),
        Exit(0) if force => {}
        Exit(0) => {
            debug!("Container version: {:?}", ver.borrow());
            let name = format!("{}.{}", container,
                &ver.borrow()[..8]);
            let finalpath = Path::new("/vagga/base/.roots")
                .join(&name);
            if finalpath.exists() {
                debug!("Path {} is already built",
                       finalpath.display());
                return Ok(name);
            }
        },
        Exit(29) => {},
        Exit(val) => return Err(format!("Builder exited with code {}", val)),
    };
    debug!("Container version: {:?}", ver.borrow());
    let tmppath = PathBuf::from(
        &format!("/vagga/base/.roots/.tmp.{}", container));
    match prepare_tmp_root_dir(&tmppath) {
        Ok(()) => {}
        Err(x) => {
            return Err(format!("Error preparing root dir: {}", x));
        }
    }
    mon.add(Rc::new("build".to_string()), Box::new(RunBuilder {
        container: container.to_string(),
        settings: wrapper.settings,
        uid_map: &uid_map,
    }));
    let result = mon.run();
    try!(unmount(&Path::new("/vagga/root")));
    try!(remove_dir(&Path::new("/vagga/root"))
        .map_err(|e| format!("Can't unlink root: {}", e)));
    try!(unmount(&Path::new("/vagga/container")));
    try!(remove_dir(&Path::new("/vagga/container"))
        .map_err(|e| format!("Can't unlink root: {}", e)));
    match result {
        Killed => return Err(format!("Builder has died")),
        Exit(0) => {},
        Exit(val) => return Err(format!("Builder exited with code {}", val)),
    };
    if ver.borrow().len() != 64 {
        mon.add(Rc::new("version".to_string()), Box::new(RunVersion {
            container: container.to_string(),
            pipe: Some(CPipe::new().ok().expect("Can't create pipe")),
            result: ver.clone(),
            settings: wrapper.settings,
            uid_map: &uid_map,
        }));
        match mon.run() {
            Killed => return Err(format!("Builder has died")),
            Exit(0) => {},
            Exit(29) => {
                return Err(format!("Internal Error: \
                        Can't version even after build"));
            },
            Exit(val) => return Err(format!("Builder exited with code {}",
                                    val)),
        };
    }
    let name = format!("{}.{}", container,
        &ver.borrow()[..8]);
    let finalpath = Path::new("/vagga/base/.roots").join(&name);
    debug!("Committing {} -> {}", tmppath.display(),
                                  finalpath.display());
    match commit_root(&tmppath, &finalpath) {
        Ok(()) => {}
        Err(x) => {
            return Err(format!("Error committing root dir: {}", x));
        }
    }
    return Ok(name);
}

pub fn build_container_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut name: String = "".to_string();
    let mut force: bool = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Internal vagga tool to setup basic system sandbox
            ");
        ap.refer(&mut name)
            .add_argument("container_name", Store,
                "Container name to build");
        ap.refer(&mut force)
            .add_option(&["--force"], StoreTrue,
                "Force build even if container is considered up to date");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));

    build_wrapper(&name, force, wrapper)
}

pub fn build_wrapper(name: &String, force: bool, wrapper: &Wrapper)
    -> Result<i32, String>
{
    let container = try!(wrapper.config.containers.get(name)
        .ok_or(format!("Container {:?} not found", name)));
    for step in container.setup.iter() {
        match step {
            &B::Container(ref name) => {
                try!(build_wrapper(name, force, wrapper)
                    .map(|x| debug!("Built container with name {}", x))
                    .map(|()| 0));
            }
            &B::SubConfig(ref cfg) => {
                match cfg.source {
                    S::Directory => {}
                    S::Container(ref name) => {
                        try!(build_wrapper(name, force, wrapper)
                            .map(|x| debug!("Built container with name {}", x))
                            .map(|()| 0));
                    }
                    S::Git(ref git) => {
                        unimplemented!();
                    }
                }
            }
            _ => {}
        }
    }

    return build_container(name, force, wrapper)
        .map(|x| debug!("Built container with name {}", x))
        .map(|()| 0);
}

pub fn print_version_hash_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut name: String = "".to_string();
    let mut short = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Prints version hash of the container without building it. If
            this command exits with code 29, then container can't be versioned
            before the build.
            ");
        ap.refer(&mut name)
            .add_argument("container_name", Store,
                "Container name to build");
        ap.refer(&mut short)
            .add_option(&["-s", "--short"], StoreTrue,
                "Print short container version, like used in directory names \
                 (8 chars)");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));
    return get_version_hash(name, wrapper)
        .map(|ver| ver
            .map(|x| if short {
                println!("{}", &x[..8])
            } else {
                println!("{}", x)
            }).map(|()| 0)
            .unwrap_or(29));
}

<commit_msg>Remove debugging print<commit_after>use std::env;
use std::rc::Rc;
use std::cell::RefCell;
use std::fs::{remove_dir_all, create_dir_all, rename};
use std::fs::{remove_file, remove_dir, PathExt};
use std::io::{stdout, stderr};
use std::os::unix::fs::symlink;
use std::path::{Path, PathBuf};

use argparse::{ArgumentParser, Store, StoreTrue};
use nix::unistd::close;
use nix::unistd::pipe;
use rustc_serialize::json;

use container::mount::{bind_mount, unmount};
use container::monitor::{Monitor, Executor, MonitorStatus};
use container::monitor::MonitorResult::{Killed, Exit};
use container::container::{Command};
use container::uidmap::{Uidmap, map_users};
use container::vagga::container_ver;
use container::pipe::CPipe;
use config::{Container, Settings};
use config::builders::Builder as B;
use config::builders::Source as S;
use super::super::file_util::create_dir;
use super::Wrapper;
use super::setup;

struct RunBuilder<'a> {
    container: String,
    settings: &'a Settings,
    uid_map: &'a Uidmap,
}

struct RunVersion<'a> {
    container: String,
    pipe: Option<CPipe>,
    result: Rc<RefCell<String>>,
    uid_map: &'a Uidmap,
    settings: &'a Settings,
}

impl<'a> Executor for RunVersion<'a> {
    fn command(&mut self) -> Command {
        let mut cmd = Command::vagga("vagga_version", "/vagga/bin/vagga");
        cmd.keep_sigmask();
        cmd.set_uidmap(self.uid_map.clone());
        cmd.arg(&self.container);
        cmd.arg("--settings");
        cmd.arg(json::encode(self.settings).unwrap());
        cmd.set_env("TERM".to_string(), "dumb".to_string());
        cmd.set_stdout_fd(self.pipe.as_ref().unwrap().writer);
        if let Ok(x) = env::var("RUST_LOG") {
            cmd.set_env("RUST_LOG".to_string(), x);
        }
        if let Ok(x) = env::var("RUST_BACKTRACE") {
            cmd.set_env("RUST_BACKTRACE".to_string(), x);
        }
        return cmd;
    }
    fn finish(&mut self, status: i32) -> MonitorStatus {
        if status == 0 {
            *self.result.borrow_mut() = String::from_utf8(
                // TODO(tailhook) graceful process of few unwraps
                self.pipe.take().unwrap().read().unwrap()).unwrap();
        }
        return MonitorStatus::Shutdown(status)
    }
}

impl<'a> Executor for RunBuilder<'a> {
    fn command(&mut self) -> Command {
        let mut cmd = Command::vagga("vagga_build", "/vagga/bin/vagga");
        cmd.keep_sigmask();
        cmd.set_uidmap(self.uid_map.clone());
        cmd.container();
        cmd.arg(&self.container);
        cmd.arg("--settings");
        cmd.arg(json::encode(self.settings).unwrap());
        cmd.set_env("TERM".to_string(),
                    env::var("TERM").unwrap_or("dumb".to_string()));
        if let Ok(x) = env::var("RUST_LOG") {
            cmd.set_env("RUST_LOG".to_string(), x);
        }
        if let Ok(x) = env::var("RUST_BACKTRACE") {
            cmd.set_env("RUST_BACKTRACE".to_string(), x);
        }
        return cmd;
    }
}

pub fn prepare_tmp_root_dir(path: &Path) -> Result<(), String> {
    if path.exists() {
        try!(remove_dir_all(path)
             .map_err(|x| format!("Error removing directory: {}", x)));
    }
    try_msg!(create_dir(path, true),
         "Error creating directory: {err}");
    let rootdir = path.join("root");
    try_msg!(create_dir(&rootdir, false),
         "Error creating directory: {err}");

    let tgtbase = Path::new("/vagga/container");
    try_msg!(create_dir(&tgtbase, false),
         "Error creating directory: {err}");
    try!(bind_mount(path, &tgtbase));

    let tgtroot = Path::new("/vagga/root");
    try_msg!(create_dir(&tgtroot, false),
         "Error creating directory: {err}");
    try!(bind_mount(&rootdir, &tgtroot));

    try_msg!(create_dir(&tgtroot.join("dev"), false),
         "Error creating directory: {err}");
    try_msg!(create_dir(&tgtroot.join("sys"), false),
         "Error creating directory: {err}");
    try_msg!(create_dir(&tgtroot.join("proc"), false),
         "Error creating directory: {err}");
    try_msg!(create_dir(&tgtroot.join("work"), false),
         "Error creating directory: {err}");
    return Ok(());
}

pub fn commit_root(tmp_path: &Path, final_path: &Path) -> Result<(), String> {
    let mut path_to_remove = None;
    if final_path.exists() {
        let rempath = tmp_path.with_file_name(
            // TODO(tailhook) consider these unwraps
            tmp_path.file_name().unwrap().to_str()
            .unwrap().to_string() + ".old");
        try!(rename(final_path, &rempath)
             .map_err(|x| format!("Error renaming old dir: {}", x)));
        path_to_remove = Some(rempath);
    }
    try!(rename(tmp_path, final_path)
         .map_err(|x| format!("Error renaming dir: {}", x)));
    if let Some(ref path_to_remove) = path_to_remove {
        try!(remove_dir_all(path_to_remove)
             .map_err(|x| format!("Error removing old dir: {}", x)));
    }
    return Ok(());
}

pub fn get_version_hash(container: String, wrapper: &Wrapper)
    -> Result<Option<String>, String>
{
    let cconfig = try!(wrapper.config.containers.get(&container)
        .ok_or(format!("Container {} not found", container)));
    let uid_map = try!(map_users(wrapper.settings,
        &cconfig.uids, &cconfig.gids));
    let mut mon = Monitor::new();
    let ver = Rc::new(RefCell::new("".to_string()));
    mon.add(Rc::new("version".to_string()), Box::new(RunVersion {
        container: container,
        pipe: Some(CPipe::new().ok().expect("Can't create pipe")),
        result: ver.clone(),
        settings: wrapper.settings,
        uid_map: &uid_map,
    }));
    match mon.run() {
        Killed => return Err(format!("Versioner has died")),
        Exit(0) => {},
        Exit(29) => return Ok(None),
        Exit(val) => return Err(format!("Versioner exited with code {}", val)),
    };
    return Ok(Some(ver.borrow().to_string()));
}

pub fn build_container(container: &String, force: bool, wrapper: &Wrapper)
    -> Result<String, String>
{
    let cconfig = try!(wrapper.config.containers.get(container)
        .ok_or(format!("Container {} not found", container)));
    let name = try!(_build_container(cconfig, container, force, wrapper));
    let destlink = Path::new("/work/.vagga").join(&container);
    let tmplink = destlink.with_extension("tmp");
    if tmplink.exists() {
        try!(remove_file(&tmplink)
            .map_err(|e| format!("Error removing temporary link: {}", e)));
    }
    let roots = if wrapper.ext_settings.storage_dir.is_some() {
        Path::new(".lnk/.roots")
    } else {
        Path::new(".roots")
    };
    let linkval = roots.join(&name).join("root");
    if cconfig.auto_clean {
        container_ver(container).map(|oldname| {
            if oldname != name {
                let base = Path::new("/vagga/base/.roots");
                let dir = base.join(&oldname);
                let tmpdir = base.join(".tmp".to_string() + &oldname);
                rename(&dir, &tmpdir)
                    .map_err(|e| error!("Error renaming old dir: {}", e));
                remove_dir_all(&tmpdir)
                    .map_err(|e| error!("Error removing old dir: {}", e));
            }
        });
    }
    try!(symlink(&linkval, &tmplink)
         .map_err(|e| format!("Error symlinking container: {}", e)));
    try!(rename(&tmplink, &destlink)
         .map_err(|e| format!("Error renaming symlink: {}", e)));
    return Ok(name);
}

pub fn _build_container(cconfig: &Container, container: &String,
    force: bool, wrapper: &Wrapper)
    -> Result<String, String>
{
    let uid_map = try!(map_users(wrapper.settings,
        &cconfig.uids, &cconfig.gids));
    let mut mon = Monitor::new();
    let ver = Rc::new(RefCell::new("".to_string()));
    mon.add(Rc::new("version".to_string()), Box::new(RunVersion {
        container: container.clone(),
        pipe: Some(CPipe::new().ok().expect("Can't create pipe")),
        result: ver.clone(),
        settings: wrapper.settings,
        uid_map: &uid_map,
    }));
    match mon.run() {
        Killed => return Err(format!("Builder has died")),
        Exit(0) if force => {}
        Exit(0) => {
            debug!("Container version: {:?}", ver.borrow());
            let name = format!("{}.{}", container,
                &ver.borrow()[..8]);
            let finalpath = Path::new("/vagga/base/.roots")
                .join(&name);
            if finalpath.exists() {
                debug!("Path {} is already built",
                       finalpath.display());
                return Ok(name);
            }
        },
        Exit(29) => {},
        Exit(val) => return Err(format!("Builder exited with code {}", val)),
    };
    debug!("Container version: {:?}", ver.borrow());
    let tmppath = PathBuf::from(
        &format!("/vagga/base/.roots/.tmp.{}", container));
    match prepare_tmp_root_dir(&tmppath) {
        Ok(()) => {}
        Err(x) => {
            return Err(format!("Error preparing root dir: {}", x));
        }
    }
    mon.add(Rc::new("build".to_string()), Box::new(RunBuilder {
        container: container.to_string(),
        settings: wrapper.settings,
        uid_map: &uid_map,
    }));
    let result = mon.run();
    try!(unmount(&Path::new("/vagga/root")));
    try!(remove_dir(&Path::new("/vagga/root"))
        .map_err(|e| format!("Can't unlink root: {}", e)));
    try!(unmount(&Path::new("/vagga/container")));
    try!(remove_dir(&Path::new("/vagga/container"))
        .map_err(|e| format!("Can't unlink root: {}", e)));
    match result {
        Killed => return Err(format!("Builder has died")),
        Exit(0) => {},
        Exit(val) => return Err(format!("Builder exited with code {}", val)),
    };
    if ver.borrow().len() != 64 {
        mon.add(Rc::new("version".to_string()), Box::new(RunVersion {
            container: container.to_string(),
            pipe: Some(CPipe::new().ok().expect("Can't create pipe")),
            result: ver.clone(),
            settings: wrapper.settings,
            uid_map: &uid_map,
        }));
        match mon.run() {
            Killed => return Err(format!("Builder has died")),
            Exit(0) => {},
            Exit(29) => {
                return Err(format!("Internal Error: \
                        Can't version even after build"));
            },
            Exit(val) => return Err(format!("Builder exited with code {}",
                                    val)),
        };
    }
    let name = format!("{}.{}", container,
        &ver.borrow()[..8]);
    let finalpath = Path::new("/vagga/base/.roots").join(&name);
    debug!("Committing {} -> {}", tmppath.display(),
                                  finalpath.display());
    match commit_root(&tmppath, &finalpath) {
        Ok(()) => {}
        Err(x) => {
            return Err(format!("Error committing root dir: {}", x));
        }
    }
    return Ok(name);
}

pub fn build_container_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut name: String = "".to_string();
    let mut force: bool = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Internal vagga tool to setup basic system sandbox
            ");
        ap.refer(&mut name)
            .add_argument("container_name", Store,
                "Container name to build");
        ap.refer(&mut force)
            .add_option(&["--force"], StoreTrue,
                "Force build even if container is considered up to date");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));

    build_wrapper(&name, force, wrapper)
}

pub fn build_wrapper(name: &String, force: bool, wrapper: &Wrapper)
    -> Result<i32, String>
{
    let container = try!(wrapper.config.containers.get(name)
        .ok_or(format!("Container {:?} not found", name)));
    for step in container.setup.iter() {
        match step {
            &B::Container(ref name) => {
                try!(build_wrapper(name, force, wrapper)
                    .map(|x| debug!("Built container with name {}", x))
                    .map(|()| 0));
            }
            &B::SubConfig(ref cfg) => {
                match cfg.source {
                    S::Directory => {}
                    S::Container(ref name) => {
                        try!(build_wrapper(name, force, wrapper)
                            .map(|x| debug!("Built container with name {}", x))
                            .map(|()| 0));
                    }
                    S::Git(ref git) => {
                        unimplemented!();
                    }
                }
            }
            _ => {}
        }
    }

    return build_container(name, force, wrapper)
        .map(|x| debug!("Built container with name {}", x))
        .map(|()| 0);
}

pub fn print_version_hash_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut name: String = "".to_string();
    let mut short = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Prints version hash of the container without building it. If
            this command exits with code 29, then container can't be versioned
            before the build.
            ");
        ap.refer(&mut name)
            .add_argument("container_name", Store,
                "Container name to build");
        ap.refer(&mut short)
            .add_option(&["-s", "--short"], StoreTrue,
                "Print short container version, like used in directory names \
                 (8 chars)");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(122);
            }
        }
    }
    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));
    return get_version_hash(name, wrapper)
        .map(|ver| ver
            .map(|x| if short {
                println!("{}", &x[..8])
            } else {
                println!("{}", x)
            }).map(|()| 0)
            .unwrap_or(29));
}

<|endoftext|>


<commit_before>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! Node manager.

use super::{encode_contract_name, to_address_vec};
use super::ContractCallExt;
use libexecutor::executor::Executor;
use rand::{Rng, SeedableRng, StdRng};
use rustc_hex::ToHex;
use std::str::FromStr;
use util::{Address, H160};

const LIST_NODE: &'static [u8] = &*b"listNode()";

lazy_static! {
    static ref LIST_NODE_ENCODED: Vec<u8> = encode_contract_name(LIST_NODE);
    static ref CONTRACT_ADDRESS: H160 = H160::from_str("00000000000000000000000000000000013241a2").unwrap();
}

pub struct NodeManager;

impl NodeManager {
    pub fn nodes(executor: &Executor) -> Vec<Address> {
        let output = executor.call_method_latest(&*CONTRACT_ADDRESS, &*LIST_NODE_ENCODED.as_slice());
        trace!("nodemanager output: {:?}", ToHex::to_hex(output.as_slice()));

        let nodes: Vec<Address> = to_address_vec(&output);
        trace!("nodemanager nodes: {:?}", nodes);
        nodes
    }

    pub fn shuffle_nodes(node_vec: &Vec<Address>, height: u64) -> Vec<Address> {
        let mut ret: Vec<Address> = vec![];

        let seed: &[_] = &[height as usize];
        let mut rng: StdRng = SeedableRng::from_seed(seed);

        for i in 0..node_vec.len() {
            let j: usize = rng.gen::<usize>() % (i + 1);
            if j != i {
                ret[i] = ret[j];
            }
            ret[j] = node_vec[i];
        }
        ret
    }
}

#[cfg(test)]
mod tests {
    extern crate logger;
    extern crate mktemp;

    use super::NodeManager;
    use std::str::FromStr;
    use tests::helpers::init_executor;
    use util::H160;

    #[test]
    fn test_node_manager_contract() {
        let executor = init_executor();
        let nodes = NodeManager::nodes(&executor);

        assert_eq!(
            nodes,
            vec![
                H160::from_str("50ad2b9d6946d9c75ae978534043e3021ee1bfb1").unwrap(),
                H160::from_str("eeb3a71c4046f63a941013f826fccc503be26b77").unwrap(),
                H160::from_str("a2bbb65d4f8c3ada29f7471abe416e18061127f3").unwrap(),
                H160::from_str("72eb1e258c9cdccebb7b62930a35cfb6ef4cd24b").unwrap(),
            ]
        )
    }
}
<commit_msg>change name<commit_after>// CITA
// Copyright 2016-2017 Cryptape Technologies LLC.

// This program is free software: you can redistribute it
// and/or modify it under the terms of the GNU 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 General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! Node manager.

use super::{encode_contract_name, to_address_vec};
use super::ContractCallExt;
use libexecutor::executor::Executor;
use rand::{Rng, SeedableRng, StdRng};
use rustc_hex::ToHex;
use std::str::FromStr;
use util::{Address, H160};

const LIST_NODE: &'static [u8] = &*b"listNode()";

lazy_static! {
    static ref LIST_NODE_ENCODED: Vec<u8> = encode_contract_name(LIST_NODE);
    static ref CONTRACT_ADDRESS: H160 = H160::from_str("00000000000000000000000000000000013241a2").unwrap();
}

pub struct NodeManager;

impl NodeManager {
    pub fn nodes(executor: &Executor) -> Vec<Address> {
        let output = executor.call_method_latest(&*CONTRACT_ADDRESS, &*LIST_NODE_ENCODED.as_slice());
        trace!("nodemanager output: {:?}", ToHex::to_hex(output.as_slice()));

        let nodes: Vec<Address> = to_address_vec(&output);
        trace!("nodemanager nodes: {:?}", nodes);
        nodes
    }

    pub fn shuffle_nodes(node_vec: &Vec<Address>, rng_seed: u64) -> Vec<Address> {
        let mut ret: Vec<Address> = vec![];

        let seed: &[_] = &[rng_seed as usize];
        let mut rng: StdRng = SeedableRng::from_seed(seed);

        for i in 0..node_vec.len() {
            let j: usize = rng.gen::<usize>() % (i + 1);
            if j != i {
                ret[i] = ret[j];
            }
            ret[j] = node_vec[i];
        }
        ret
    }
}

#[cfg(test)]
mod tests {
    extern crate logger;
    extern crate mktemp;

    use super::NodeManager;
    use std::str::FromStr;
    use tests::helpers::init_executor;
    use util::H160;

    #[test]
    fn test_node_manager_contract() {
        let executor = init_executor();
        let nodes = NodeManager::nodes(&executor);

        assert_eq!(
            nodes,
            vec![
                H160::from_str("50ad2b9d6946d9c75ae978534043e3021ee1bfb1").unwrap(),
                H160::from_str("eeb3a71c4046f63a941013f826fccc503be26b77").unwrap(),
                H160::from_str("a2bbb65d4f8c3ada29f7471abe416e18061127f3").unwrap(),
                H160::from_str("72eb1e258c9cdccebb7b62930a35cfb6ef4cd24b").unwrap(),
            ]
        )
    }
}
<|endoftext|>


<commit_before>use svm::cell::SVMCell;

use self::ExprNode::*;
use self::NumNode::*;
use super::ForkTable;

type SymTable<'a> = ForkTable<'a, &'a str, (usize,usize)>;

static INDENT: &'static str = "\t";

/// Trait for AST nodes.
pub trait ASTNode {
    /// Compile this node to a list of SVM expressions
    fn compile(self)                    -> Result<SVMCell, &'static str>;

    /// Pretty-print this node
    fn prettyprint(&self)               -> String { self.print_level(0usize) }

    /// Pretty-print this node at the desired indent level
    fn print_level(&self, level: usize) -> String;
}

/// Expression
///
/// All Seax Scheme expressions are one of the following
///
///  + Nested S-Expressions
///  + Identifiers
///  + Lists
///  + Numbers
///     - signed int
///     - unsigned int
///     - floating-point
///  + Characters
///  + Strings
///
///  TODO: implement the entire Scheme 'numeric tower'
///  TODO: macros should happen
///  TODO: figure out quasiquote somehow.
#[derive(Clone, PartialEq,Debug)]
pub enum ExprNode {
    Root(RootNode),
    SExpr(SExprNode),
    Name(NameNode),
    ListConst(ListNode),
    NumConst(NumNode),
    BoolConst(BoolNode),
    StringConst(StringNode),
    CharConst(CharNode),
}

impl ASTNode for ExprNode {
    fn compile(self) -> Result<SVMCell, &'static str> {
        match self {
            Root(node)          => node.compile(),
            SExpr(node)         => node.compile(),
            Name(node)          => node.compile(),
            ListConst(node)     => node.compile(),
            NumConst(node)      => node.compile(),
            BoolConst(node)     => node.compile(),
            CharConst(node)     => node.compile(),
            StringConst(node)   => node.compile()
        }
    }

    fn print_level(&self, level: usize) -> String {
        match *self {
            Root(ref node)          => node.print_level(level),
            SExpr(ref node)         => node.print_level(level),
            Name(ref node)          => node.print_level(level),
            ListConst(ref node)     => node.print_level(level),
            NumConst(ref node)      => node.print_level(level),
            BoolConst(ref node)     => node.print_level(level),
            CharConst(ref node)     => node.print_level(level),
            StringConst(ref node)   => node.print_level(level)
        }
    }
}

#[derive(Clone, PartialEq,Debug)]
pub enum NumNode {
    IntConst(IntNode),
    UIntConst(UIntNode),
    FloatConst(FloatNode)
}

/// AST node for the root of a program's AST
#[derive(Clone, PartialEq,Debug)]
pub struct RootNode { pub exprs: Vec<ExprNode> }

impl ASTNode for RootNode {
    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        self.exprs
            .iter()
            .fold(
                String::new(),
                |mut s, i| {
                    s.push_str(i.print_level(level + 1).as_ref());
                    s
                })
    }

}

/// AST node for an S-expression.
///
/// This includes function application, assignment,
/// function definition, et cetera...Scheme is not a complexl anguage.
#[derive(Clone, PartialEq,Debug)]
pub struct SExprNode {
    pub operator: NameNode,
    pub operands: Vec<ExprNode>,
}

impl ASTNode for SExprNode {

    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }

    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level { tab.push_str(INDENT); };

        let mut result = String::new();
        result.push_str("S-Expression:\n");
        tab.push_str(INDENT);

        // op
        result.push_str(tab.as_ref());
        result.push_str("Operator:\n");
        result.push_str(self.operator.print_level(level + 1).as_ref());
        result.push('\n');

        for operand in self.operands.iter() {
            result.push_str(tab.as_ref());
            result.push_str("Operand: \n");
            result.push_str(operand.print_level(level + 1).as_ref());
            result.push('\n');
        };
        result
    }

}

/// AST node for a list literal
#[derive(Clone, PartialEq,Debug)]
pub struct ListNode { pub elements: Vec<ExprNode> }

impl ASTNode for ListNode {
    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT); };

        let mut result = String::new();
        result.push_str("List:\n");
        tab.push_str(INDENT);

        for elem in self.elements.iter() {
            result.push_str(tab.as_ref());
            result.push_str(elem.print_level(level + 1).as_ref());
            result.push('\n');
        };
        result
    }

}

/// AST node for an identifier
#[derive(Clone, PartialEq,Debug)]
pub struct NameNode { pub name: String }

impl ASTNode for NameNode {
    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT)};

        let mut result = String::new();
        result.push_str(tab.as_ref());
        result.push_str("Name: ");
        result.push_str(self.name.as_ref());
        result.push_str("\n");

        result
    }

}

/// AST node for an integer constant
#[derive(Clone, PartialEq,Debug)]
pub struct IntNode { pub value: isize }

impl ASTNode for NumNode {
    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str(tab.as_ref());
        result.push_str("Number: ");

        match *self {
            NumNode::UIntConst(ref node) => {
                result.push_str(format!("{}u", node.value).as_ref());
                result.push_str("\n");
            },
            NumNode::IntConst(ref node) => {
                result.push_str(format!("{}", node.value).as_ref());
                result.push_str("\n");
            },
            NumNode::FloatConst(ref node) => {
                result.push_str(format!("{}f", node.value).as_ref());
                result.push_str("\n");
            }
        }
        result
    }
}

/// AST node for an unsigned integer constant
#[derive(Clone, PartialEq,Debug)]
pub struct UIntNode { pub value: usize }

/// AST node for a floating-point constant
#[derive(Clone, PartialEq,Debug)]
pub struct FloatNode { pub value: f64 }

/// AST node for a boolean constant
#[derive(Clone, PartialEq,Debug)]
pub struct BoolNode { pub value: bool }

impl ASTNode for BoolNode {

    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }

    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str(tab.as_ref());
        result.push_str("Boolean: ");
        result.push_str(format!("{}", self.value).as_ref());
        result.push_str("\n");
        result
    }
}

/// AST node for a character constant
#[derive(Clone, PartialEq,Debug)]
pub struct CharNode { pub value: char }

impl ASTNode for CharNode {
    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str("Character: \'");
        result.push(self.value);
        result.push_str("\'\n");
        result
    }
}

/// AST node for a  string constant
#[derive(Clone, PartialEq,Debug)]
pub struct StringNode { pub value: String }

impl ASTNode for StringNode {
    fn compile(self) -> Result<SVMCell, &'static str> {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str("String: \"");
        result.push_str(self.value.as_ref());
        result.push_str("\"\n");
        result
    }
}

<commit_msg>Started working on code gen<commit_after>use svm::cell::SVMCell;

use self::ExprNode::*;
use self::NumNode::*;
use super::ForkTable;

type SymTable<'a>   = ForkTable<'a, &'a str, (usize,usize)>;
type CompileResult  = Result<SVMCell, &'static str>;

static INDENT: &'static str = "\t";

/// Trait for AST nodes.
pub trait ASTNode {
    /// Compile this node to a list of SVM expressions
    fn compile(self, state: SymTable)   -> CompileResult;

    /// Pretty-print this node
    fn prettyprint(&self)               -> String { self.print_level(0usize) }

    /// Pretty-print this node at the desired indent level
    fn print_level(&self, level: usize) -> String;
}

/// Expression
///
/// All Seax Scheme expressions are one of the following
///
///  + Nested S-Expressions
///  + Identifiers
///  + Lists
///  + Numbers
///     - signed int
///     - unsigned int
///     - floating-point
///  + Characters
///  + Strings
///
///  TODO: implement the entire Scheme 'numeric tower'
///  TODO: macros should happen
///  TODO: figure out quasiquote somehow.
#[derive(Clone, PartialEq,Debug)]
pub enum ExprNode {
    Root(RootNode),
    SExpr(SExprNode),
    Name(NameNode),
    ListConst(ListNode),
    NumConst(NumNode),
    BoolConst(BoolNode),
    StringConst(StringNode),
    CharConst(CharNode),
}

impl ASTNode for ExprNode {
    fn compile(self,state)              -> CompileResult {
        match self {
            Root(node)          => node.compile(),
            SExpr(node)         => node.compile(),
            Name(node)          => node.compile(),
            ListConst(node)     => node.compile(),
            NumConst(node)      => node.compile(),
            BoolConst(node)     => node.compile(),
            CharConst(node)     => node.compile(),
            StringConst(node)   => node.compile()
        }
    }

    fn print_level(&self, level: usize) -> String {
        match *self {
            Root(ref node)          => node.print_level(level),
            SExpr(ref node)         => node.print_level(level),
            Name(ref node)          => node.print_level(level),
            ListConst(ref node)     => node.print_level(level),
            NumConst(ref node)      => node.print_level(level),
            BoolConst(ref node)     => node.print_level(level),
            CharConst(ref node)     => node.print_level(level),
            StringConst(ref node)   => node.print_level(level)
        }
    }
}

#[derive(Clone, PartialEq,Debug)]
pub enum NumNode {
    IntConst(IntNode),
    UIntConst(UIntNode),
    FloatConst(FloatNode)
}

/// AST node for the root of a program's AST
#[derive(Clone, PartialEq,Debug)]
pub struct RootNode { pub exprs: Vec<ExprNode> }

impl ASTNode for RootNode {
    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        self.exprs
            .iter()
            .fold(
                String::new(),
                |mut s, i| {
                    s.push_str(i.print_level(level + 1).as_ref());
                    s
                })
    }

}

/// AST node for an S-expression.
///
/// This includes function application, assignment,
/// function definition, et cetera...Scheme is not a complexl anguage.
#[derive(Clone, PartialEq,Debug)]
pub struct SExprNode {
    pub operator: NameNode,
    pub operands: Vec<ExprNode>,
}

impl ASTNode for SExprNode {

    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }

    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level { tab.push_str(INDENT); };

        let mut result = String::new();
        result.push_str("S-Expression:\n");
        tab.push_str(INDENT);

        // op
        result.push_str(tab.as_ref());
        result.push_str("Operator:\n");
        result.push_str(self.operator.print_level(level + 1).as_ref());
        result.push('\n');

        for operand in self.operands.iter() {
            result.push_str(tab.as_ref());
            result.push_str("Operand: \n");
            result.push_str(operand.print_level(level + 1).as_ref());
            result.push('\n');
        };
        result
    }

}

/// AST node for a list literal
#[derive(Clone, PartialEq,Debug)]
pub struct ListNode { pub elements: Vec<ExprNode> }

impl ASTNode for ListNode {
    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT); };

        let mut result = String::new();
        result.push_str("List:\n");
        tab.push_str(INDENT);

        for elem in self.elements.iter() {
            result.push_str(tab.as_ref());
            result.push_str(elem.print_level(level + 1).as_ref());
            result.push('\n');
        };
        result
    }

}

/// AST node for an identifier
#[derive(Clone, PartialEq,Debug)]
pub struct NameNode { pub name: String }

impl ASTNode for NameNode {
    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT)};

        let mut result = String::new();
        result.push_str(tab.as_ref());
        result.push_str("Name: ");
        result.push_str(self.name.as_ref());
        result.push_str("\n");

        result
    }

}

/// AST node for an integer constant
#[derive(Clone, PartialEq,Debug)]
pub struct IntNode { pub value: isize }

impl ASTNode for NumNode {
    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str(tab.as_ref());
        result.push_str("Number: ");

        match *self {
            NumNode::UIntConst(ref node) => {
                result.push_str(format!("{}u", node.value).as_ref());
                result.push_str("\n");
            },
            NumNode::IntConst(ref node) => {
                result.push_str(format!("{}", node.value).as_ref());
                result.push_str("\n");
            },
            NumNode::FloatConst(ref node) => {
                result.push_str(format!("{}f", node.value).as_ref());
                result.push_str("\n");
            }
        }
        result
    }
}

/// AST node for an unsigned integer constant
#[derive(Clone, PartialEq,Debug)]
pub struct UIntNode { pub value: usize }

/// AST node for a floating-point constant
#[derive(Clone, PartialEq,Debug)]
pub struct FloatNode { pub value: f64 }

/// AST node for a boolean constant
#[derive(Clone, PartialEq,Debug)]
pub struct BoolNode { pub value: bool }

impl ASTNode for BoolNode {

    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }

    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str(tab.as_ref());
        result.push_str("Boolean: ");
        result.push_str(format!("{}", self.value).as_ref());
        result.push_str("\n");
        result
    }
}

/// AST node for a character constant
#[derive(Clone, PartialEq,Debug)]
pub struct CharNode { pub value: char }

impl ASTNode for CharNode {
    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str("Character: \'");
        result.push(self.value);
        result.push_str("\'\n");
        result
    }
}

/// AST node for a  string constant
#[derive(Clone, PartialEq,Debug)]
pub struct StringNode { pub value: String }

impl ASTNode for StringNode {
    fn compile(self,state: SymTable)    -> CompileResult {
        Err("UNINPLEMENTED")
    }
    fn print_level(&self, level: usize) -> String {
        let mut tab = String::new();
        for _ in 0 .. level {tab.push_str(INDENT);};

        let mut result = String::new();

        result.push_str("String: \"");
        result.push_str(self.value.as_ref());
        result.push_str("\"\n");
        result
    }
}

<|endoftext|>


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

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword("dominant-baseline",
                 """auto use-script no-change reset-size ideographic alphabetic hanging
                    mathematical central middle text-after-edge text-before-edge""",
                 products="gecko",
                 animation_value_type="discrete",
                 spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty")}

${helpers.single_keyword("vector-effect", "none non-scaling-stroke",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty")}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color",
    "RGBAColor",
    "RGBA::new(0, 0, 0, 255)",
    products="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
)}

${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
                          products="gecko",
                          animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity")}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color",
    "RGBAColor",
    "RGBA::new(0, 0, 0, 255)",
    products="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
)}

${helpers.predefined_type("flood-opacity", "Opacity",
                          "1.0", products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}

${helpers.predefined_type(
    "lighting-color",
    "RGBAColor",
    "RGBA::new(255, 255, 255, 255)",
    products="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
)}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword("mask-type", "luminance alpha",
                         products="gecko", animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")}

${helpers.predefined_type(
    "clip-path",
    "basic_shape::ClippingShape",
    "generics::basic_shape::ShapeSource::None",
    products="gecko",
    boxed=True,
    animation_value_type="ComputedValue",
    flags="CREATES_STACKING_CONTEXT",
    spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)}

${helpers.single_keyword("mask-mode",
                         "match-source alpha luminance",
                         vector=True,
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")}

${helpers.predefined_type(
    "mask-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
    vector=True,
)}

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type(
        "mask-position-" + axis,
        "position::" + direction + "Position",
        products="gecko",
        extra_prefixes="webkit",
        initial_value="computed::LengthOrPercentage::zero()",
        initial_specified_value="specified::PositionComponent::Center",
        spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
        animation_value_type="ComputedValue",
        vector_animation_type="repeatable_list",
        vector=True,
    )}
% endfor

${helpers.single_keyword(
    "mask-clip",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box no-clip",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
)}

${helpers.single_keyword(
    "mask-origin",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
)}

${helpers.predefined_type(
    "mask-size",
    "background::BackgroundSize",
    "computed::BackgroundSize::auto()",
    initial_specified_value="specified::BackgroundSize::auto()",
    products="gecko",
    extra_prefixes="webkit",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
    animation_value_type="MaskSizeList",
    vector=True,
    vector_animation_type="repeatable_list",
)}

${helpers.single_keyword("mask-composite",
                         "add subtract intersect exclude",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")}

${helpers.predefined_type("mask-image", "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    flags="CREATES_STACKING_CONTEXT")}
<commit_msg>style: Change nscolor to StyleComplexColor in SVG properties.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword("dominant-baseline",
                 """auto use-script no-change reset-size ideographic alphabetic hanging
                    mathematical central middle text-after-edge text-before-edge""",
                 products="gecko",
                 animation_value_type="discrete",
                 spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty")}

${helpers.single_keyword("vector-effect", "none non-scaling-stroke",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty")}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    products="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
)}

${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
                          products="gecko",
                          animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity")}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    products="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
)}

${helpers.predefined_type("flood-opacity", "Opacity",
                          "1.0", products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}

${helpers.predefined_type(
    "lighting-color",
    "Color",
    "RGBA::new(255, 255, 255, 255).into()",
    products="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
)}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword("mask-type", "luminance alpha",
                         products="gecko", animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")}

${helpers.predefined_type(
    "clip-path",
    "basic_shape::ClippingShape",
    "generics::basic_shape::ShapeSource::None",
    products="gecko",
    boxed=True,
    animation_value_type="ComputedValue",
    flags="CREATES_STACKING_CONTEXT",
    spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)}

${helpers.single_keyword("mask-mode",
                         "match-source alpha luminance",
                         vector=True,
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")}

${helpers.predefined_type(
    "mask-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
    vector=True,
)}

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type(
        "mask-position-" + axis,
        "position::" + direction + "Position",
        products="gecko",
        extra_prefixes="webkit",
        initial_value="computed::LengthOrPercentage::zero()",
        initial_specified_value="specified::PositionComponent::Center",
        spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
        animation_value_type="ComputedValue",
        vector_animation_type="repeatable_list",
        vector=True,
    )}
% endfor

${helpers.single_keyword(
    "mask-clip",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box no-clip",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
)}

${helpers.single_keyword(
    "mask-origin",
    "border-box content-box padding-box",
    extra_gecko_values="fill-box stroke-box view-box",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
)}

${helpers.predefined_type(
    "mask-size",
    "background::BackgroundSize",
    "computed::BackgroundSize::auto()",
    initial_specified_value="specified::BackgroundSize::auto()",
    products="gecko",
    extra_prefixes="webkit",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
    animation_value_type="MaskSizeList",
    vector=True,
    vector_animation_type="repeatable_list",
)}

${helpers.single_keyword("mask-composite",
                         "add subtract intersect exclude",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")}

${helpers.predefined_type("mask-image", "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    flags="CREATES_STACKING_CONTEXT")}
<|endoftext|>