<commit_before>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}
struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    pub fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        let result = Exec::cmd("sshfs")
            .arg("-o")
            .arg("allow_other")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("sudo")
            .arg("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    // `locked_device` is necessary as an external argument because no
    // device mapping exists in a pre-unlock world, so we need to target
    // the loop device directly.
    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unlock")
            .arg("-b")
            .arg(locked_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unlock {}: ``{}''",
                locked_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // No extra argument is needed here (c.f. `self::unlock()`) because
    // we can go straight after the well-known device available in
    // /dev/disk/by-label.
    pub fn lock(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("lock")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to lock {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("mount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to mount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unmount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unmount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn main_impl() -> Result<(), AuricError> {
    println!("Hello there!");
    Ok(())
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<commit_msg>apricot-auric: Complete scaffolding<commit_after>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    pub fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        let result = Exec::cmd("sshfs")
            .arg("-o")
            .arg("allow_other")
            .arg(RSYNC_DOT_NET_REMOTE_DIR)
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "sshfs failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("fusermount")
            .arg("-u")
            .arg(&self.mountpoint)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "fusermount -u failed: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let result = Exec::cmd("losetup")
            .arg("-l")
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to list loop devices: ``{}''",
                result.stderr_str()
            )));
        }

        for line in result.stdout_str().lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("loop-setup")
            .arg("-f")
            .arg(&self.backing)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to attach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        let result = Exec::cmd("udisksctl")
            .arg("loop-delete")
            .arg("-b")
            .arg(&attached_loop)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to detach loop device: ``{}''",
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    // `locked_device` is necessary as an external argument because no
    // device mapping exists in a pre-unlock world, so we need to target
    // the loop device directly.
    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unlock")
            .arg("-b")
            .arg(locked_device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unlock {}: ``{}''",
                locked_device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    // No extra argument is needed here (c.f. `self::unlock()`) because
    // we can go straight after the well-known device available in
    // /dev/disk/by-label.
    pub fn lock(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("lock")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to lock {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("mount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to mount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        let result = Exec::cmd("udisksctl")
            .arg("unmount")
            .arg("-b")
            .arg(&self.device)
            .stdout(Redirection::Pipe)
            .stderr(Redirection::Pipe)
            .capture()?;
        if !result.success() {
            return Err(AuricError::Subprocess(format!(
                "failed to unmount {}: ``{}''",
                self.device.to_str().unwrap(),
                result.stderr_str()
            )));
        }
        Ok(())
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        self.sshfs_manager.mount()?;
        self.loop_manager.attach()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.unlock(&loop_device)?;
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        self.luks_manager.unmount()?;
        self.luks_manager.lock()?;
        self.loop_manager.detach()?;
        self.sshfs_manager.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<|endoftext|>


<commit_before>use cssparser::Parser;

use parsers::{CssParserExt, Parse, ParseError};

#[derive(Eq, PartialEq)]
pub enum NumberListLength {
    Exact(usize),
    Unbounded,
}

#[derive(Debug, PartialEq)]
pub enum NumberListError {
    IncorrectNumberOfElements,
    Parse(ParseError),
}

#[derive(Debug, PartialEq)]
pub struct NumberList(pub Vec<f64>);

impl Parse for NumberList {
    type Data = NumberListLength;
    type Err = NumberListError;

    fn parse(
        parser: &mut Parser<'_, '_>,
        length: NumberListLength,
    ) -> Result<NumberList, NumberListError> {
        let mut v = match length {
            NumberListLength::Exact(l) if l > 0 => Vec::<f64>::with_capacity(l),
            NumberListLength::Exact(_) => unreachable!(),
            NumberListLength::Unbounded => Vec::<f64>::new(),
        };

        if parser.is_exhausted() && length == NumberListLength::Unbounded {
            return Ok(NumberList(v));
        }

        for i in 0.. {
            if i != 0 {
                parser.optional_comma();
            }

            v.push(f64::from(parser.expect_finite_number().map_err(|_| {
                NumberListError::Parse(ParseError::new("expected number"))
            })?));

            if let NumberListLength::Exact(l) = length {
                if i + 1 == l {
                    break;
                }
            }

            if parser.is_exhausted() {
                match length {
                    NumberListLength::Exact(l) => {
                        if i + 1 == l {
                            break;
                        }
                    }
                    _ => break,
                }
            }
        }

        parser
            .expect_exhausted()
            .map_err(|_| NumberListError::IncorrectNumberOfElements)?;

        Ok(NumberList(v))
    }
}

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

    #[test]
    fn parses_number_list() {
        assert_eq!(
            NumberList::parse_str("5", NumberListLength::Exact(1)),
            Ok(NumberList(vec![5.0]))
        );

        assert_eq!(
            NumberList::parse_str("1 2 3 4", NumberListLength::Exact(4)),
            Ok(NumberList(vec![1.0, 2.0, 3.0, 4.0]))
        );

        assert_eq!(
            NumberList::parse_str("", NumberListLength::Unbounded),
            Ok(NumberList(vec![]))
        );

        assert_eq!(
            NumberList::parse_str("1, 2, 3.0, 4, 5", NumberListLength::Unbounded),
            Ok(NumberList(vec![1.0, 2.0, 3.0, 4.0, 5.0]))
        );
    }

    #[test]
    fn errors_on_invalid_number_list() {
        // empty
        assert!(NumberList::parse_str("", NumberListLength::Exact(1)).is_err());

        // garbage
        assert!(NumberList::parse_str("foo", NumberListLength::Exact(1)).is_err());
        assert!(NumberList::parse_str("1foo", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1 foo", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1 foo 2", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1,foo", NumberListLength::Exact(2)).is_err());

        // too many
        assert!(NumberList::parse_str("1 2", NumberListLength::Exact(1)).is_err());

        // extra token
        assert!(NumberList::parse_str("1,", NumberListLength::Exact(1)).is_err());
        assert!(NumberList::parse_str("1,", NumberListLength::Exact(1)).is_err());
        assert!(NumberList::parse_str("1,", NumberListLength::Unbounded).is_err());

        // too few
        assert!(NumberList::parse_str("1", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1 2", NumberListLength::Exact(3)).is_err());
    }
}
<commit_msg>NumberList: Don't use the Parse trait; implement similar methods directly<commit_after>use cssparser::{Parser, ParserInput};

use parsers::{CssParserExt, Parse, ParseError};

#[derive(Eq, PartialEq)]
pub enum NumberListLength {
    Exact(usize),
    Unbounded,
}

#[derive(Debug, PartialEq)]
pub enum NumberListError {
    IncorrectNumberOfElements,
    Parse(ParseError),
}

#[derive(Debug, PartialEq)]
pub struct NumberList(pub Vec<f64>);

impl NumberList {
    pub fn parse(
        parser: &mut Parser<'_, '_>,
        length: NumberListLength,
    ) -> Result<NumberList, NumberListError> {
        let mut v = match length {
            NumberListLength::Exact(l) if l > 0 => Vec::<f64>::with_capacity(l),
            NumberListLength::Exact(_) => unreachable!(),
            NumberListLength::Unbounded => Vec::<f64>::new(),
        };

        if parser.is_exhausted() && length == NumberListLength::Unbounded {
            return Ok(NumberList(v));
        }

        for i in 0.. {
            if i != 0 {
                parser.optional_comma();
            }

            v.push(f64::from(parser.expect_finite_number().map_err(|_| {
                NumberListError::Parse(ParseError::new("expected number"))
            })?));

            if let NumberListLength::Exact(l) = length {
                if i + 1 == l {
                    break;
                }
            }

            if parser.is_exhausted() {
                match length {
                    NumberListLength::Exact(l) => {
                        if i + 1 == l {
                            break;
                        }
                    }
                    _ => break,
                }
            }
        }

        parser
            .expect_exhausted()
            .map_err(|_| NumberListError::IncorrectNumberOfElements)?;

        Ok(NumberList(v))
    }

    pub fn parse_str(s: &str, length: NumberListLength) -> Result<NumberList, NumberListError> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse(&mut parser, length).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

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

    #[test]
    fn parses_number_list() {
        assert_eq!(
            NumberList::parse_str("5", NumberListLength::Exact(1)),
            Ok(NumberList(vec![5.0]))
        );

        assert_eq!(
            NumberList::parse_str("1 2 3 4", NumberListLength::Exact(4)),
            Ok(NumberList(vec![1.0, 2.0, 3.0, 4.0]))
        );

        assert_eq!(
            NumberList::parse_str("", NumberListLength::Unbounded),
            Ok(NumberList(vec![]))
        );

        assert_eq!(
            NumberList::parse_str("1, 2, 3.0, 4, 5", NumberListLength::Unbounded),
            Ok(NumberList(vec![1.0, 2.0, 3.0, 4.0, 5.0]))
        );
    }

    #[test]
    fn errors_on_invalid_number_list() {
        // empty
        assert!(NumberList::parse_str("", NumberListLength::Exact(1)).is_err());

        // garbage
        assert!(NumberList::parse_str("foo", NumberListLength::Exact(1)).is_err());
        assert!(NumberList::parse_str("1foo", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1 foo", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1 foo 2", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1,foo", NumberListLength::Exact(2)).is_err());

        // too many
        assert!(NumberList::parse_str("1 2", NumberListLength::Exact(1)).is_err());

        // extra token
        assert!(NumberList::parse_str("1,", NumberListLength::Exact(1)).is_err());
        assert!(NumberList::parse_str("1,", NumberListLength::Exact(1)).is_err());
        assert!(NumberList::parse_str("1,", NumberListLength::Unbounded).is_err());

        // too few
        assert!(NumberList::parse_str("1", NumberListLength::Exact(2)).is_err());
        assert!(NumberList::parse_str("1 2", NumberListLength::Exact(3)).is_err());
    }
}
<|endoftext|>


<commit_before>use platform::drivers::vga;

use core::option::{Some, None};
use core::{slice, str};
use core::iter::Iterator;

pub fn putc(c: u8) {
  unsafe {
    putchar(vga::curr_x, vga::curr_y, c);
    vga::curr_x += 1;
    if vga::curr_x > vga::WIDTH {
        vga::curr_x -= vga::WIDTH;
        vga::curr_y += 1;
    }
  }
}

pub fn putchar(x: u16, y: u16, c: u8) {
  if x >= vga::WIDTH || y >= vga::HEIGHT {
    return;
  }
  let idx : uint =  (y * vga::WIDTH * 2 + x * 2) as uint;
  unsafe {
    *((vga::ADDRESS + idx) as *mut u16) = vga::make_vgaentry(c, vga::fg_color, vga::bg_color);
  }
}

pub fn newline() {
  unsafe {
    vga::curr_x = 0;
    vga::curr_y += 1;
  }
}

pub fn write(s: &str) {
  for b in slice::iter(str::as_bytes(s)) {
    putc(*b);
  }
}

<commit_msg>remove prefix from variable usage<commit_after>use platform::drivers::vga::{ADDRESS, WIDTH, HEIGHT, make_vgaentry, fg_color, bg_color, curr_x, curr_y};
use core::option::{Some, None};
use core::{slice, str};
use core::iter::Iterator;

pub fn putc(c: u8) {
  unsafe {
    putchar(curr_x, curr_y, c);
    curr_x += 1;
    if curr_x > WIDTH {
        curr_x -= WIDTH;
        curr_y += 1;
    }
  }
}

pub fn putchar(x: u16, y: u16, c: u8) {
  if x >= WIDTH || y >= HEIGHT {
    return;
  }
  let idx : uint =  (y * WIDTH * 2 + x * 2) as uint;
  unsafe {
    *((ADDRESS + idx) as *mut u16) = make_vgaentry(c, fg_color, bg_color);
  }
}

pub fn newline() {
  unsafe {
    curr_x = 0;
    curr_y += 1;
  }
}

pub fn write(s: &str) {
  for b in slice::iter(str::as_bytes(s)) {
    putc(*b);
  }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

use std::env;
use std::path::PathBuf;

use crate::argument::Argument;
use crate::core::*;
use crate::tmux::Tmux;

mod argument;
mod config;
mod container;
#[macro_use]
mod core;
mod nmk;
mod pathenv;
mod platform;
mod terminal;
mod time;
mod tmux;
mod zsh;

fn get_unicode() -> &'static str {
    match platform::get() {
        platform::PlatformType::OSX => "en_US.UTF-8",
        _ => "C.UTF-8",
    }
}

fn setup_path(arg: &Argument, nmk_dir: &PathBuf) {
    const PATH: &str = "PATH";
    let mut p = pathenv::PathVec::parse(env::var_os(PATH).expect("$PATH not found"));
    p.push_front(nmk_dir.join("local").join("bin"));
    p.push_front(nmk_dir.join("bin"));
    p = p.unique().no_version_managers();
    if arg.debug {
        for (i, path) in p.iter().enumerate() {
            debug!("{}[{}]={:?}", PATH, i + 1, path);
        }
    }
    env::set_var(PATH, p.make());
}

fn setup_environment(arg: &Argument, nmk_dir: &PathBuf, tmux: &Tmux, unicode_name: &str) {
    let init_vim = nmk_dir.join("vim").join("init.vim");
    let zdotdir = nmk_dir.join("zsh");
    set_env("NMK_DIR", nmk_dir);

    tmux.setup(arg);
    if let Some(path) = init_vim
        .to_str()
        .map(|s|
            s.to_string().replace(" ", r"\ ")
        ) {
        set_env("VIMINIT", format!("source {}", path));
    }
    set_env("ZDOTDIR", zdotdir);

    env::remove_var("VIRTUAL_ENV");
    const LANG: &str = "LANG";
    let lang_defined = || env::var_os(LANG).is_some();
    if arg.unicode || (arg.autofix && !lang_defined()) {
        set_env(LANG, unicode_name);
    }

    if arg.force_unicode {
        set_env("LC_ALL", unicode_name);
    }

    set_env("NMK_BIN", env::current_exe().unwrap());
}

fn setup_prefer_editor() {
    const EDITOR: &str = "EDITOR";
    if env::var_os(EDITOR).is_none() {
        let mut editors = ["nvim", "vim"].iter();
        if let Some(editor) = editors.find(|bin| which::which(bin).is_ok()) {
            set_env(EDITOR, editor);
            debug!("using {} as prefer editor", editor);
        }
    }
}

fn unset_temp_env(config: config::Config) {
    for name in config.tmux_setting_envs {
        env::remove_var(name);
    }
}

fn main() {
    let start = std::time::Instant::now();
    let unicode_name = get_unicode();
    let arg = argument::parse(unicode_name);
    let verbosity = if arg.debug { 3 } else { 1 };
    stderrlog::new()
        .module(module_path!())
        .verbosity(verbosity)
        .init()
        .expect("Cannot setup logger");
    debug!("{:#?}", arg);

    if arg.ssh {
        nmk::display_motd();
    }

    let nmk_dir = nmk::dir();
    let tmux_dir = tmux::dir(&nmk_dir);
    debug!("nmk_dir: {:?}, tmux_dir: {:?}", nmk_dir, tmux_dir);
    nmk::add_local_library(&nmk_dir);
    setup_path(&arg, &nmk_dir);

    let tmux = Tmux::new(&nmk_dir, tmux_dir);
    debug!("using tmux version {}", tmux.version());

    terminal::setup(&arg);
    setup_environment(&arg, &nmk_dir, &tmux, unicode_name);
    zsh::setup(&arg, &nmk_dir);
    setup_prefer_editor();
    if arg.login {
        unset_temp_env(config::load(&nmk_dir));
        tmux.login_shell(arg, start);
    } else {
        tmux.exec(arg, start);
    }
}
<commit_msg>nmk.rs: use config by reference<commit_after>#[macro_use]
extern crate log;

use std::env;
use std::path::PathBuf;

use crate::argument::Argument;
use crate::core::*;
use crate::tmux::Tmux;

mod argument;
mod config;
mod container;
#[macro_use]
mod core;
mod nmk;
mod pathenv;
mod platform;
mod terminal;
mod time;
mod tmux;
mod zsh;

fn get_unicode() -> &'static str {
    match platform::get() {
        platform::PlatformType::OSX => "en_US.UTF-8",
        _ => "C.UTF-8",
    }
}

fn setup_path(arg: &Argument, nmk_dir: &PathBuf) {
    const PATH: &str = "PATH";
    let mut p = pathenv::PathVec::parse(env::var_os(PATH).expect("$PATH not found"));
    p.push_front(nmk_dir.join("local").join("bin"));
    p.push_front(nmk_dir.join("bin"));
    p = p.unique().no_version_managers();
    if arg.debug {
        for (i, path) in p.iter().enumerate() {
            debug!("{}[{}]={:?}", PATH, i + 1, path);
        }
    }
    env::set_var(PATH, p.make());
}

fn setup_environment(arg: &Argument, nmk_dir: &PathBuf, tmux: &Tmux, unicode_name: &str) {
    let init_vim = nmk_dir.join("vim").join("init.vim");
    let zdotdir = nmk_dir.join("zsh");
    set_env("NMK_DIR", nmk_dir);

    tmux.setup(arg);
    if let Some(path) = init_vim
        .to_str()
        .map(|s|
            s.to_string().replace(" ", r"\ ")
        ) {
        set_env("VIMINIT", format!("source {}", path));
    }
    set_env("ZDOTDIR", zdotdir);

    env::remove_var("VIRTUAL_ENV");
    const LANG: &str = "LANG";
    let lang_defined = || env::var_os(LANG).is_some();
    if arg.unicode || (arg.autofix && !lang_defined()) {
        set_env(LANG, unicode_name);
    }

    if arg.force_unicode {
        set_env("LC_ALL", unicode_name);
    }

    set_env("NMK_BIN", env::current_exe().unwrap());
}

fn setup_prefer_editor() {
    const EDITOR: &str = "EDITOR";
    if env::var_os(EDITOR).is_none() {
        let mut editors = ["nvim", "vim"].iter();
        if let Some(editor) = editors.find(|bin| which::which(bin).is_ok()) {
            set_env(EDITOR, editor);
            debug!("using {} as prefer editor", editor);
        }
    }
}

fn unset_temp_env(config: &config::Config) {
    for name in config.tmux_setting_envs.iter() {
        env::remove_var(name);
    }
}

fn main() {
    let start = std::time::Instant::now();
    let unicode_name = get_unicode();
    let arg = argument::parse(unicode_name);
    let verbosity = if arg.debug { 3 } else { 1 };
    stderrlog::new()
        .module(module_path!())
        .verbosity(verbosity)
        .init()
        .expect("Cannot setup logger");
    debug!("{:#?}", arg);

    if arg.ssh {
        nmk::display_motd();
    }

    let nmk_dir = nmk::dir();
    let tmux_dir = tmux::dir(&nmk_dir);
    debug!("nmk_dir: {:?}, tmux_dir: {:?}", nmk_dir, tmux_dir);
    nmk::add_local_library(&nmk_dir);
    setup_path(&arg, &nmk_dir);

    let tmux = Tmux::new(&nmk_dir, tmux_dir);
    debug!("using tmux version {}", tmux.version());

    terminal::setup(&arg);
    setup_environment(&arg, &nmk_dir, &tmux, unicode_name);
    zsh::setup(&arg, &nmk_dir);
    setup_prefer_editor();
    if arg.login {
        let config = config::load(&nmk_dir);
        unset_temp_env(&config);
        tmux.login_shell(arg, start);
    } else {
        tmux.exec(arg, start);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use openssl::ssl::{ Ssl, SslContext, SslMethod, SSL_VERIFY_NONE };
use openssl::ssl::error::SslError;
use openssl::x509::X509FileType;
use openssl_sys;

use std::collections::HashMap;
use std::io::Error;
use std::path::Path;
use std::sync::{ Arc, RwLock };

use certificate_record::CertificateRecord;

pub trait SslContextProvider : Send + Sync {
    fn context(&self) -> Result<SslContext, Error>;
    fn update(&self, HashMap<String, CertificateRecord>) -> ();
}

#[derive(Clone)]
pub struct SniSslContextProvider {
    main_context: Arc<RwLock<SslContext>>
}

impl SslContextProvider for SniSslContextProvider {
    fn context(&self) -> Result<SslContext, Error> {
        Ok(checklock!(self.main_context.read()).clone())
    }

    fn update(&self, configured_hosts: HashMap<String, CertificateRecord>) -> () {
        debug!("Updating SniSslContextProvider");

        let mut new_ssl_hosts = HashMap::new();

        for record in configured_hosts.values() {
            debug!("Creating SslContext for {}", record.hostname);
            let ssl_context = create_ssl_context(
                &record.cert_file, &record.private_key_file, &record.full_chain);

            if ssl_context.is_ok() {
                let ssl_context = ssl_context.unwrap();
                new_ssl_hosts.insert(record.hostname.clone(), ssl_context);
            } else {
                error!("Failed to configure SslContext for: {:?}", record);
            }
        }

        debug!("Update certificates");
        checklock!(self.main_context.write())
            .set_servername_callback_with_data(SniSslContextProvider::servername_callback, new_ssl_hosts);
    }
}

impl SniSslContextProvider {
    pub fn new() -> Self {
        SniSslContextProvider {
            main_context: Arc::new(
                              RwLock::new(
                                  SslContext::new(SslMethod::Sslv23).unwrap()
                              )
                          )
        }
    }

    fn servername_callback_impl<T>(ssl: &mut SslForSni<T>,
                                   configured_certs: &HashMap<String, T>) -> i32 {
        debug!("servername_callback invoked");
        let requested_hostname = ssl.get_hostname();

        if requested_hostname.is_none() {
            debug!("No SNI - ");
            return openssl_sys::SSL_TLSEXT_ERR_NOACK;
        }

        let requested_hostname = requested_hostname.unwrap();

        debug!("Selecting certificate for host {}", requested_hostname);

        let ssl_context_for_hostname = configured_certs.get(&requested_hostname);

        if let Some(ctx)= ssl_context_for_hostname {
            ssl.set_context(ctx);
        }

        openssl_sys::SSL_TLSEXT_ERR_OK
    }

    fn servername_callback(ssl: &mut Ssl, _: &mut i32,
                           configured_certs: &HashMap<String, SslContext>)
        -> i32 {
        Self::servername_callback_impl(ssl, configured_certs)
    }
}

impl Default for SniSslContextProvider {
    fn default() -> Self {
        SniSslContextProvider::new()
    }
}

pub trait SslForSni<T> {
    fn get_hostname(&self) -> Option<String>;
    fn set_context(&self, ctx: &T) -> Option<T>;
}

impl SslForSni<SslContext> for Ssl {
    fn get_hostname(&self) -> Option<String> {
        self.get_servername()
    }

    fn set_context(&self, ctx: &SslContext) -> Option<SslContext> {
        Some(self.set_ssl_context(ctx))
    }
}

pub fn create_ssl_context<C, K>(crt: &C, key: &K, chain: &Option<K>)
        -> Result<SslContext, SslError>
    where C: AsRef<Path>, K: AsRef<Path> {

    debug!("Creating SSL Context with Cert: {:?}, Key: {:?}",
            crt.as_ref().to_str(), key.as_ref().to_str());

    let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
    try!(ctx.set_cipher_list("DEFAULT"));
    try!(ctx.set_certificate_file(crt.as_ref(), X509FileType::PEM));
    try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM));

    if let Some(ref chain_file) = *chain {
        try!(ctx.set_certificate_chain_file(chain_file.as_ref(), X509FileType::PEM));
    }

    ctx.set_verify(SSL_VERIFY_NONE, None);

    Ok(ctx)
}

#[cfg(test)]
mod sni_ssl_context_provider {
    use openssl_sys;
    use std::collections::HashMap;
    use std::sync::mpsc::{ channel, Sender };

    use super::*;

    pub struct MockSsl {
        servername: Option<String>,
        context_set: Option<Sender<String>>
    }

    impl SslForSni<String> for MockSsl {
        fn get_hostname(&self) -> Option<String> {
            self.servername.clone()
        }

        fn set_context(&self, ctx: &String) -> Option<String> {
            if let Some(ref context_set) = self.context_set {
                context_set.send(ctx.clone()).unwrap();
            }

            None
        }
    }

    #[test]
    fn should_set_context_based_on_servername() {
        let (tx_context_called, rx_context_called) = channel();

        let mut ssl = MockSsl {
            servername: Some("test.knilxof.org".to_owned()),
            context_set: Some(tx_context_called)
        };

        let mut contexts = HashMap::new();

        contexts.insert("test.knilxof.org".to_owned(), "fake_context".to_owned());

        let result = SniSslContextProvider::servername_callback_impl(&mut ssl, &contexts);

        assert!(
            result == openssl_sys::SSL_TLSEXT_ERR_OK,
            "Servername callback did not return OK"
        );
        assert!(
            rx_context_called.recv().unwrap() == "fake_context",
            "Set context was not called with the expected value"
        );
    }

    #[test]
    fn should_return_fail_code_if_servername_is_not_available() {
        let mut ssl = MockSsl {
            servername: None,
            context_set: None,
        };

        let contexts = HashMap::new();
        let result = SniSslContextProvider::servername_callback_impl(&mut ssl, &contexts);

        assert!(
            result == openssl_sys::SSL_TLSEXT_ERR_NOACK,
            "Expected ERR_NOACK result from servername callback"
        );
    }
}
<commit_msg>A better error message when the hostname used to make a request does not have a certificate (#419)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use openssl::ssl::{ Ssl, SslContext, SslMethod, SSL_VERIFY_NONE };
use openssl::ssl::error::SslError;
use openssl::x509::X509FileType;
use openssl_sys;

use std::collections::HashMap;
use std::io::Error;
use std::path::Path;
use std::sync::{ Arc, RwLock };

use certificate_record::CertificateRecord;

pub trait SslContextProvider : Send + Sync {
    fn context(&self) -> Result<SslContext, Error>;
    fn update(&self, HashMap<String, CertificateRecord>) -> ();
}

#[derive(Clone)]
pub struct SniSslContextProvider {
    main_context: Arc<RwLock<SslContext>>
}

impl SslContextProvider for SniSslContextProvider {
    fn context(&self) -> Result<SslContext, Error> {
        Ok(checklock!(self.main_context.read()).clone())
    }

    fn update(&self, configured_hosts: HashMap<String, CertificateRecord>) -> () {
        debug!("Updating SniSslContextProvider");

        let mut new_ssl_hosts = HashMap::new();

        for record in configured_hosts.values() {
            debug!("Creating SslContext for {}", record.hostname);
            let ssl_context = create_ssl_context(
                &record.cert_file, &record.private_key_file, &record.full_chain);

            if ssl_context.is_ok() {
                let ssl_context = ssl_context.unwrap();
                new_ssl_hosts.insert(record.hostname.clone(), ssl_context);
            } else {
                error!("Failed to configure SslContext for: {:?}", record);
            }
        }

        debug!("Update certificates");
        checklock!(self.main_context.write())
            .set_servername_callback_with_data(SniSslContextProvider::servername_callback, new_ssl_hosts);
    }
}

impl SniSslContextProvider {
    pub fn new() -> Self {
        SniSslContextProvider {
            main_context: Arc::new(
                              RwLock::new(
                                  SslContext::new(SslMethod::Sslv23).unwrap()
                              )
                          )
        }
    }

    fn servername_callback_impl<T>(ssl: &mut SslForSni<T>,
                                   configured_certs: &HashMap<String, T>) -> i32 {
        debug!("servername_callback invoked");
        let requested_hostname = ssl.get_hostname();

        if requested_hostname.is_none() {
            error!("No SNI information sent from client - client unsupported");
            return openssl_sys::SSL_TLSEXT_ERR_NOACK;
        }

        let requested_hostname = requested_hostname.unwrap();

        debug!("Selecting certificate for host {}", requested_hostname);

        let ssl_context_for_hostname = configured_certs.get(&requested_hostname);

        if let Some(ctx)= ssl_context_for_hostname {
            ssl.set_context(ctx);
        } else {
            error!("No certificate available for requested hostname: {}", requested_hostname);
        }

        openssl_sys::SSL_TLSEXT_ERR_OK
    }

    fn servername_callback(ssl: &mut Ssl, _: &mut i32,
                           configured_certs: &HashMap<String, SslContext>)
        -> i32 {
        Self::servername_callback_impl(ssl, configured_certs)
    }
}

impl Default for SniSslContextProvider {
    fn default() -> Self {
        SniSslContextProvider::new()
    }
}

pub trait SslForSni<T> {
    fn get_hostname(&self) -> Option<String>;
    fn set_context(&self, ctx: &T) -> Option<T>;
}

impl SslForSni<SslContext> for Ssl {
    fn get_hostname(&self) -> Option<String> {
        self.get_servername()
    }

    fn set_context(&self, ctx: &SslContext) -> Option<SslContext> {
        Some(self.set_ssl_context(ctx))
    }
}

pub fn create_ssl_context<C, K>(crt: &C, key: &K, chain: &Option<K>)
        -> Result<SslContext, SslError>
    where C: AsRef<Path>, K: AsRef<Path> {

    debug!("Creating SSL Context with Cert: {:?}, Key: {:?}",
            crt.as_ref().to_str(), key.as_ref().to_str());

    let mut ctx = try!(SslContext::new(SslMethod::Sslv23));
    try!(ctx.set_cipher_list("DEFAULT"));
    try!(ctx.set_certificate_file(crt.as_ref(), X509FileType::PEM));
    try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM));

    if let Some(ref chain_file) = *chain {
        try!(ctx.set_certificate_chain_file(chain_file.as_ref(), X509FileType::PEM));
    }

    ctx.set_verify(SSL_VERIFY_NONE, None);

    Ok(ctx)
}

#[cfg(test)]
mod sni_ssl_context_provider {
    use openssl_sys;
    use std::collections::HashMap;
    use std::sync::mpsc::{ channel, Sender };

    use super::*;

    pub struct MockSsl {
        servername: Option<String>,
        context_set: Option<Sender<String>>
    }

    impl SslForSni<String> for MockSsl {
        fn get_hostname(&self) -> Option<String> {
            self.servername.clone()
        }

        fn set_context(&self, ctx: &String) -> Option<String> {
            if let Some(ref context_set) = self.context_set {
                context_set.send(ctx.clone()).unwrap();
            }

            None
        }
    }

    #[test]
    fn should_set_context_based_on_servername() {
        let (tx_context_called, rx_context_called) = channel();

        let mut ssl = MockSsl {
            servername: Some("test.knilxof.org".to_owned()),
            context_set: Some(tx_context_called)
        };

        let mut contexts = HashMap::new();

        contexts.insert("test.knilxof.org".to_owned(), "fake_context".to_owned());

        let result = SniSslContextProvider::servername_callback_impl(&mut ssl, &contexts);

        assert!(
            result == openssl_sys::SSL_TLSEXT_ERR_OK,
            "Servername callback did not return OK"
        );
        assert!(
            rx_context_called.recv().unwrap() == "fake_context",
            "Set context was not called with the expected value"
        );
    }

    #[test]
    fn should_return_fail_code_if_servername_is_not_available() {
        let mut ssl = MockSsl {
            servername: None,
            context_set: None,
        };

        let contexts = HashMap::new();
        let result = SniSslContextProvider::servername_callback_impl(&mut ssl, &contexts);

        assert!(
            result == openssl_sys::SSL_TLSEXT_ERR_NOACK,
            "Expected ERR_NOACK result from servername callback"
        );
    }
}
<|endoftext|>


<commit_before>//! Procedurally generating the game world.

use world::{Chunk, ChunkIndex, ChunkProvider, HeightType, HexPillar};
use world::{CHUNK_SIZE, GroundMaterial, PILLAR_STEP_HEIGHT, PillarSection, Prop, PropType};
use rand::Rand;
use gen::PlantGenerator;
use noise::{PermutationTable, open_simplex2, open_simplex3};
use gen::seeded_rng;

enum Biome {
    GrassLand,
    Desert,
    Snow,
    Forest,
    RainForest,
    Savanna,
    Stone,
    WeihnachtsmannLand,
}

/// Land "fill noise" scaling in x, y, and z direction.
const LAND_NOISE_SCALE: (f32, f32, f32) = (0.03, 0.03, 0.05);

/// Main type to generate the game world. Implements the `ChunkProvider` trait
/// (TODO, see #8).
pub struct WorldGenerator {
    seed: u64,
    terrain_table: PermutationTable,
    plant_table: PermutationTable,
    temperature_table: PermutationTable,
    humidity_table: PermutationTable,
}

impl WorldGenerator {
    /// Creates the generator with the given seed.
    pub fn with_seed(seed: u64) -> Self {
        let mut terrain_rng = seeded_rng(seed, 0, ());
        let mut plant_rng = seeded_rng(seed, 1, ());
        let mut temperature_rng = seeded_rng(seed, 2, ());
        let mut humidity_rng = seeded_rng(seed, 3, ());

        WorldGenerator {
            seed: seed,
            terrain_table: PermutationTable::rand(&mut terrain_rng),
            plant_table: PermutationTable::rand(&mut plant_rng),
            temperature_table: PermutationTable::rand(&mut temperature_rng),
            humidity_table: PermutationTable::rand(&mut humidity_rng),
        }
    }

    /// Returns the seed of this world generator.
    pub fn seed(&self) -> u64 {
        self.seed
    }

    fn biome_from_climate(temperature: f32, humidity: f32) -> Biome {
        match (temperature, humidity) {
            (0.0...0.2, 0.0...0.2) => Biome::Stone,
            (0.0...0.2, 0.2...0.4) => Biome::Snow,
            (0.0...0.2, 0.4...1.0) => Biome::Snow,
            (0.2...0.4, 0.0...0.2) => Biome::GrassLand,
            (0.2...0.4, 0.2...0.4) => Biome::GrassLand,
            (0.2...0.4, 0.4...1.0) => Biome::Forest,
            (0.4...1.0, 0.0...0.2) => Biome::Desert,
            (0.4...1.0, 0.2...0.4) => Biome::Savanna,
            (0.4...1.0, 0.4...1.0) => Biome::RainForest,
            _ => Biome::WeihnachtsmannLand,

        }
    }

    fn plant_threshold_from_biome(biome: &Biome) -> f32 {
        0.05 +
        match *biome {
            Biome::GrassLand => 0.3,
            Biome::Desert => 0.465,
            Biome::Snow => 0.3,
            Biome::Forest => 0.2,
            Biome::RainForest => 0.1,
            Biome::Savanna => 0.375,
            Biome::Stone => 0.45,
            Biome::WeihnachtsmannLand => 1.0,
        }
    }

    fn steepness_from_biome(biome: &Biome) -> f32 {
        match *biome {
            Biome::GrassLand => 40.0,
            Biome::Desert => 200.0,
            Biome::Snow => 30.0,
            Biome::Forest => 35.0,
            Biome::RainForest => 25.0,
            Biome::Savanna => 50.0,
            Biome::Stone => 15.0,
            Biome::WeihnachtsmannLand => 5.0,
        }
    }

    fn material_from_biome(biome: &Biome) -> GroundMaterial {
        match *biome {
            Biome::GrassLand => GroundMaterial::Grass,
            Biome::Desert => GroundMaterial::Sand,
            Biome::Snow => GroundMaterial::Snow,
            Biome::Forest => GroundMaterial::Mulch,
            Biome::RainForest => GroundMaterial::Jungle,
            Biome::Savanna => GroundMaterial::Dirt,
            Biome::Stone => GroundMaterial::Stone,
            Biome::WeihnachtsmannLand => GroundMaterial::Color(1.0, 0.0, 0.0),
        }
    }
}

impl ChunkProvider for WorldGenerator {
    fn load_chunk(&self, index: ChunkIndex) -> Option<Chunk> {
        const WORLDGEN_HEIGHT: usize = 256;
        // Create a 3D-Array of booleans indicating which pillar sections to fill
        // (Map height is unlimited in theory, but we'll limit worldgen to 64 height
        // units)
        let mut fill = [[[false; WORLDGEN_HEIGHT]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize];

        Some(Chunk::with_pillars(index, |pos| {

            let real_pos = pos.to_real();
            let x = real_pos.x;
            let y = real_pos.y;
            // Pillar pos relative to first pillar
            let rel_q = pos.q - index.0.q * CHUNK_SIZE as i32;
            let rel_r = pos.r - index.0.r * CHUNK_SIZE as i32;

            // noises
            let mut temperature_noise = (open_simplex2::<f32>(&self.temperature_table,
                                                              &[(x as f32) * 0.0015,
                                                                (y as f32) * 0.0015]) +
                                         0.6) / 2.0;
            temperature_noise += 0.05 *
                                 open_simplex2::<f32>(&self.temperature_table,
                                                      &[(x as f32) * 0.15, (y as f32) * 0.15]);

            let mut humidity_noise = (open_simplex2::<f32>(&self.humidity_table,
                                                           &[(x as f32) * 0.0015,
                                                             (y as f32) * 0.0015]) +
                                      0.6) / 2.0;
            humidity_noise += 0.05 *
                              open_simplex2::<f32>(&self.humidity_table,
                                                   &[(x as f32) * 0.15, (y as f32) * 0.15]);

            let current_biome = WorldGenerator::biome_from_climate(temperature_noise,
                                                                   humidity_noise);

            for i in 0..WORLDGEN_HEIGHT {
                if i == 0 {
                    fill[rel_q as usize][rel_r as usize][i as usize] = true;
                    continue;
                }

                let z = i as f32 * PILLAR_STEP_HEIGHT;
                let fill_noise = open_simplex3::<f32>(&self.terrain_table,
                                                      &[x * LAND_NOISE_SCALE.0,
                                                        y * LAND_NOISE_SCALE.1,
                                                        z * LAND_NOISE_SCALE.2]);

                // The noise is (theoretically) in the range -1..1
                // Map the noise to a range of 0..1
                let fill_noise = (fill_noise + 1.0) / 2.0;

                // Calculate threshold to fill this "block". The lower the threshold, the more
                // likely this voxel is filled, so it should increase with height.
                let height_pct = i as f32 / WORLDGEN_HEIGHT as f32;

                // The threshold is calculated using a sigmoid function. These are the
                // parameters used:

                /// Minimum threshold to prevent threshold to reach 0,
                /// needed to have any caves at all
                const MIN_THRESH: f32 = 0.6;
                /// Threshold at half value (max. steepness, avg. terrain
                /// height)
                const THRESH_MID: f32 = 0.5;

                // "Steepness" of the sigmoid function.
                let thresh_steepness: f32 = WorldGenerator::steepness_from_biome(&current_biome);

                let sig_thresh = 1.0 /
                                 (1.0 + f32::exp(-thresh_steepness * (height_pct - THRESH_MID)));

                let threshold = (sig_thresh + MIN_THRESH) / (1.0 + MIN_THRESH);

                fill[rel_q as usize][rel_r as usize][i as usize] = fill_noise > threshold;
            }

            let column = &fill[rel_q as usize][rel_r as usize];

            // Create sections for all connected `true`s in the array
            let mut sections = Vec::new();
            let mut low = 0;
            let mut height = None;
            for i in 0..WORLDGEN_HEIGHT {
                let material = WorldGenerator::material_from_biome(&current_biome);

                match (height, column[i]) {

                    (Some(h), true) => {
                        // Next one's still solid, increase section height
                        height = Some(h + 1);
                    }
                    (Some(h), false) => {
                        // Create a section of height `h` and start over
                        sections.push(PillarSection::new(material,
                                                         HeightType::from_units(low),
                                                         HeightType::from_units(low + h)));
                        height = None;
                    }
                    (None, true) => {
                        low = i as u16;
                        height = Some(1);
                    }
                    (None, false) => {}
                };
            }

            if let Some(h) = height {
                // Create the topmost pillar
                sections.push(PillarSection::new(GroundMaterial::Dirt,
                                                 HeightType::from_units(low),
                                                 HeightType::from_units(low + h)));
            }

            let mut props = Vec::new();

            // plants
            let plant_noise = open_simplex2::<f32>(&self.plant_table,
                                                   &[(x as f32) * 0.25, (y as f32) * 0.25]);

            if plant_noise > 1.0 {
                // WorldGenerator::plant_threshold_from_biome(&current_biome) {
                let mut rng = super::seeded_rng(self.seed, "TREE", (pos.q, pos.r));
                let gen = PlantGenerator::rand(&mut rng);

                // put the tree at the highest position
                let height = match sections.last() {
                    Some(section) => section.top,
                    None => HeightType::from_units(0),
                };

                props.push(Prop {
                    baseline: height,
                    prop: PropType::Plant(gen.generate(&mut rng)),
                });
            }

            HexPillar::new(sections, props)
        }))
    }

    fn is_chunk_loadable(&self, _: ChunkIndex) -> bool {
        // All chunks can be generated from nothing
        true
    }
}
<commit_msg>smooth out biome edges<commit_after>//! Procedurally generating the game world.

use world::{Chunk, ChunkIndex, ChunkProvider, HeightType, HexPillar};
use world::{CHUNK_SIZE, GroundMaterial, PILLAR_STEP_HEIGHT, PillarSection, Prop, PropType};
use rand::Rand;
use gen::PlantGenerator;
use noise::{PermutationTable, open_simplex2, open_simplex3};
use gen::seeded_rng;

enum Biome {
    GrassLand,
    Desert,
    Snow,
    Forest,
    RainForest,
    Savanna,
    Stone,
    WeihnachtsmannLand,
}

/// Land "fill noise" scaling in x, y, and z direction.
const LAND_NOISE_SCALE: (f32, f32, f32) = (0.03, 0.03, 0.05);

/// Main type to generate the game world. Implements the `ChunkProvider` trait
/// (TODO, see #8).
pub struct WorldGenerator {
    seed: u64,
    terrain_table: PermutationTable,
    plant_table: PermutationTable,
    temperature_table: PermutationTable,
    humidity_table: PermutationTable,
}

impl WorldGenerator {
    /// Creates the generator with the given seed.
    pub fn with_seed(seed: u64) -> Self {
        let mut terrain_rng = seeded_rng(seed, 0, ());
        let mut plant_rng = seeded_rng(seed, 1, ());
        let mut temperature_rng = seeded_rng(seed, 2, ());
        let mut humidity_rng = seeded_rng(seed, 3, ());

        WorldGenerator {
            seed: seed,
            terrain_table: PermutationTable::rand(&mut terrain_rng),
            plant_table: PermutationTable::rand(&mut plant_rng),
            temperature_table: PermutationTable::rand(&mut temperature_rng),
            humidity_table: PermutationTable::rand(&mut humidity_rng),
        }
    }

    /// Returns the seed of this world generator.
    pub fn seed(&self) -> u64 {
        self.seed
    }

    fn biome_from_climate(temperature: f32, humidity: f32) -> Biome {
        match (temperature, humidity) {
            (0.0...0.2, 0.0...0.2) => Biome::Stone,
            (0.0...0.2, 0.2...0.4) => Biome::Snow,
            (0.0...0.2, 0.4...1.0) => Biome::Snow,
            (0.2...0.4, 0.0...0.2) => Biome::GrassLand,
            (0.2...0.4, 0.2...0.4) => Biome::GrassLand,
            (0.2...0.4, 0.4...1.0) => Biome::Forest,
            (0.4...1.0, 0.0...0.2) => Biome::Desert,
            (0.4...1.0, 0.2...0.4) => Biome::Savanna,
            (0.4...1.0, 0.4...1.0) => Biome::RainForest,
            _ => Biome::WeihnachtsmannLand,

        }
    }

    fn plant_threshold_from_biome(biome: &Biome) -> f32 {
        0.05 +
        match *biome {
            Biome::GrassLand => 0.3,
            Biome::Desert => 0.46,
            Biome::Snow => 0.35,
            Biome::Forest => 0.25,
            Biome::RainForest => 0.2,
            Biome::Savanna => 0.375,
            Biome::Stone => 0.45,
            Biome::WeihnachtsmannLand => 1.0,
        }
    }

    /// trying aproximate the
    /// steepvalues       10   20   60  200
    /// for temperature  0.0  0.2  0.4  1.0
    fn steepness_from_temperature(temperature: f32) -> f32 {
        120.0 * temperature * temperature + 72.0 * temperature + 3.5
    }

    fn material_from_biome(biome: &Biome) -> GroundMaterial {
        match *biome {
            Biome::GrassLand => GroundMaterial::Grass,
            Biome::Desert => GroundMaterial::Sand,
            Biome::Snow => GroundMaterial::Snow,
            Biome::Forest => GroundMaterial::Mulch,
            Biome::RainForest => GroundMaterial::Jungle,
            Biome::Savanna => GroundMaterial::Dirt,
            Biome::Stone => GroundMaterial::Stone,
            Biome::WeihnachtsmannLand => GroundMaterial::Color(1.0, 0.0, 0.0),
        }
    }
}

impl ChunkProvider for WorldGenerator {
    fn load_chunk(&self, index: ChunkIndex) -> Option<Chunk> {
        const WORLDGEN_HEIGHT: usize = 256;
        // Create a 3D-Array of booleans indicating which pillar sections to fill
        // (Map height is unlimited in theory, but we'll limit worldgen to 64 height
        // units)
        let mut fill = [[[false; WORLDGEN_HEIGHT]; CHUNK_SIZE as usize]; CHUNK_SIZE as usize];

        Some(Chunk::with_pillars(index, |pos| {

            let real_pos = pos.to_real();
            let x = real_pos.x;
            let y = real_pos.y;
            // Pillar pos relative to first pillar
            let rel_q = pos.q - index.0.q * CHUNK_SIZE as i32;
            let rel_r = pos.r - index.0.r * CHUNK_SIZE as i32;

            // noises
            let mut temperature_noise = (open_simplex2::<f32>(&self.temperature_table,
                                                              &[(x as f32) * 0.0015,
                                                                (y as f32) * 0.0015]) +
                                         0.6) / 2.0;
            temperature_noise += 0.035 *
                                 open_simplex2::<f32>(&self.temperature_table,
                                                      &[(x as f32) * 0.15, (y as f32) * 0.15]);

            let mut humidity_noise = (open_simplex2::<f32>(&self.humidity_table,
                                                           &[(x as f32) * 0.0015,
                                                             (y as f32) * 0.0015]) +
                                      0.6) / 2.0;
            humidity_noise += 0.035 *
                              open_simplex2::<f32>(&self.humidity_table,
                                                   &[(x as f32) * 0.15, (y as f32) * 0.15]);

            let current_biome = WorldGenerator::biome_from_climate(temperature_noise,
                                                                   humidity_noise);

            for i in 0..WORLDGEN_HEIGHT {
                if i == 0 {
                    fill[rel_q as usize][rel_r as usize][i as usize] = true;
                    continue;
                }

                let z = i as f32 * PILLAR_STEP_HEIGHT;
                let fill_noise = open_simplex3::<f32>(&self.terrain_table,
                                                      &[x * LAND_NOISE_SCALE.0,
                                                        y * LAND_NOISE_SCALE.1,
                                                        z * LAND_NOISE_SCALE.2]);

                // The noise is (theoretically) in the range -1..1
                // Map the noise to a range of 0..1
                let fill_noise = (fill_noise + 1.0) / 2.0;

                // Calculate threshold to fill this "block". The lower the threshold, the more
                // likely this voxel is filled, so it should increase with height.
                let height_pct = i as f32 / WORLDGEN_HEIGHT as f32;

                // The threshold is calculated using a sigmoid function. These are the
                // parameters used:

                /// Minimum threshold to prevent threshold to reach 0,
                /// needed to have any caves at all
                const MIN_THRESH: f32 = 0.6;
                /// Threshold at half value (max. steepness, avg. terrain
                /// height)
                const THRESH_MID: f32 = 0.5;

                // "Steepness" of the sigmoid function.
                let thresh_steepness: f32 =
                    WorldGenerator::steepness_from_temperature(temperature_noise);

                let sig_thresh = 1.0 /
                                 (1.0 + f32::exp(-thresh_steepness * (height_pct - THRESH_MID)));

                let threshold = (sig_thresh + MIN_THRESH) / (1.0 + MIN_THRESH);

                fill[rel_q as usize][rel_r as usize][i as usize] = fill_noise > threshold;
            }

            let column = &fill[rel_q as usize][rel_r as usize];

            // Create sections for all connected `true`s in the array
            let mut sections = Vec::new();
            let mut low = 0;
            let mut height = None;
            for i in 0..WORLDGEN_HEIGHT {
                let material = WorldGenerator::material_from_biome(&current_biome);

                match (height, column[i]) {

                    (Some(h), true) => {
                        // Next one's still solid, increase section height
                        height = Some(h + 1);
                    }
                    (Some(h), false) => {
                        // Create a section of height `h` and start over
                        sections.push(PillarSection::new(material,
                                                         HeightType::from_units(low),
                                                         HeightType::from_units(low + h)));
                        height = None;
                    }
                    (None, true) => {
                        low = i as u16;
                        height = Some(1);
                    }
                    (None, false) => {}
                };
            }

            if let Some(h) = height {
                // Create the topmost pillar
                sections.push(PillarSection::new(GroundMaterial::Dirt,
                                                 HeightType::from_units(low),
                                                 HeightType::from_units(low + h)));
            }

            let mut props = Vec::new();

            // plants
            let plant_noise = open_simplex2::<f32>(&self.plant_table,
                                                   &[(x as f32) * 0.25, (y as f32) * 0.25]);

            if plant_noise > WorldGenerator::plant_threshold_from_biome(&current_biome) {
                let mut rng = super::seeded_rng(self.seed, "TREE", (pos.q, pos.r));
                let gen = PlantGenerator::rand(&mut rng);

                // put the tree at the highest position
                let height = match sections.last() {
                    Some(section) => section.top,
                    None => HeightType::from_units(0),
                };

                props.push(Prop {
                    baseline: height,
                    prop: PropType::Plant(gen.generate(&mut rng)),
                });
            }

            HexPillar::new(sections, props)
        }))
    }

    fn is_chunk_loadable(&self, _: ChunkIndex) -> bool {
        // All chunks can be generated from nothing
        true
    }
}
<|endoftext|>


<commit_before>enum merge_comp<T> {
    lt,
    gt,
    eq(T)
}

fn merge<T: copy>(vec1: [T], vec2: [T], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    let result = [];
    let i1 = 0u, i2 = 0u;
    let len1 = vec::len(vec1), len2 = vec::len(vec2);
    while (i1 < len1 && i2 < len2) {
        alt comp(vec1[i1], vec2[i2]) {
          lt    { result += [ vec1[i1] ]; i1 += 1u; }
          gt    { result += [ vec2[i2] ]; i2 += 1u;}
          eq(x) { result += [ x ]; i1 += 1u; i2 += 1u; }
        }
    }
    if i1 < len1 {
        result += vec::slice(vec1, i1, len1);
    }
    if i2 < len2 {
        result += vec::slice(vec2, i2, len2);
    }
    ret result;
}

fn mergei<T: copy>(vecs: [[T]], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    ret alt vec::len(vecs) {
      0u { [] }
      1u { vecs[0] }
      l  {
        let pre  = mergei(vec::slice(vecs, 0u64, l / 2u64), comp);
        let post = mergei(vec::slice(vecs, l / 2u64, l), comp);
        ret merge(pre, post, comp);
      }
    }
}

fn zip_default<T: copy, U: copy>(v1: [const T], v2: [const U], def: (T, U)) -> [(T, U)] {
    let result = [];
    let (l1, l2) = (vec::len(v1), vec::len(v2));
    let (d1, d2) = def;
    uint::range(0u, uint::max(l1, l2)) { |i|
        let e1 = if i < l1 { v1[i] } else { d1 };
        let e2 = if i < l2 { v2[i] } else { d2 };
        result += [(e1, e2)];
    }
    ret result;
}

fn div_rem(n: uint, d: uint) -> (uint, uint) {
    (n / d, n % d)
}

#[cfg(test)]
mod tests {
    fn merge_add_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, e1 + e2));
    }

    fn merge_max_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, int::max(e1, e2)));
    }

    #[test]
    fn test_merge() {
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_add_comp) ==
            [(1, 3), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_max_comp) ==
            [(1, 2), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [], merge_add_comp) == [(1, 1), (3, 1), (5, 1)];
        assert merge([], [], merge_add_comp) == [];
    }

    #[test]
    fn test_mergei() {
        assert mergei([[], [], []], merge_add_comp) == [];
        assert mergei([[(1, 1), (2, 1)], [(1, 2), (3, 1)], [(-1, 3)]], merge_add_comp) ==
            [(-1, 3), (1, 3), (2, 1), (3, 1)];
        assert mergei([[(1, 1)], [(1, 2)], [(1, 3)]], merge_add_comp) == [(1, 6)];
    }

    #[test]
    fn test_zip_default() {
        assert zip_default([1, 2, 3], [4u, 5u, 6u], (0, 0u)) == [(1, 4u), (2, 5u), (3, 6u)];
        assert zip_default([1, 2, 3], [4u], (0, 0u)) == [(1, 4u), (2, 0u), (3, 0u)];
        assert zip_default([], [], (0, 0u)) == [];
    }

    #[test]
    fn test_div_mod() {
        let (d, m) = div_mod(1234u, 56u);
        assert d * 56u + m == 1234u;
    }
}
<commit_msg>Modify div_rem"s testset<commit_after>enum merge_comp<T> {
    lt,
    gt,
    eq(T)
}

fn merge<T: copy>(vec1: [T], vec2: [T], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    let result = [];
    let i1 = 0u, i2 = 0u;
    let len1 = vec::len(vec1), len2 = vec::len(vec2);
    while (i1 < len1 && i2 < len2) {
        alt comp(vec1[i1], vec2[i2]) {
          lt    { result += [ vec1[i1] ]; i1 += 1u; }
          gt    { result += [ vec2[i2] ]; i2 += 1u;}
          eq(x) { result += [ x ]; i1 += 1u; i2 += 1u; }
        }
    }
    if i1 < len1 {
        result += vec::slice(vec1, i1, len1);
    }
    if i2 < len2 {
        result += vec::slice(vec2, i2, len2);
    }
    ret result;
}

fn mergei<T: copy>(vecs: [[T]], comp: fn(T, T) -> merge_comp<T>) -> [T] {
    ret alt vec::len(vecs) {
      0u { [] }
      1u { vecs[0] }
      l  {
        let pre  = mergei(vec::slice(vecs, 0u64, l / 2u64), comp);
        let post = mergei(vec::slice(vecs, l / 2u64, l), comp);
        ret merge(pre, post, comp);
      }
    }
}

fn zip_default<T: copy, U: copy>(v1: [const T], v2: [const U], def: (T, U)) -> [(T, U)] {
    let result = [];
    let (l1, l2) = (vec::len(v1), vec::len(v2));
    let (d1, d2) = def;
    uint::range(0u, uint::max(l1, l2)) { |i|
        let e1 = if i < l1 { v1[i] } else { d1 };
        let e2 = if i < l2 { v2[i] } else { d2 };
        result += [(e1, e2)];
    }
    ret result;
}

fn div_rem(n: uint, d: uint) -> (uint, uint) {
    (n / d, n % d)
}

#[cfg(test)]
mod tests {
    fn merge_add_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, e1 + e2));
    }

    fn merge_max_comp(e1: (int, int), e2: (int, int)) -> merge_comp<(int, int)> {
        let ((n1, e1), (n2, e2)) = (e1, e2);
        if n1 < n2 { ret lt; }
        if n1 > n2 { ret gt; }
        ret eq((n1, int::max(e1, e2)));
    }

    #[test]
    fn test_merge() {
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_add_comp) ==
            [(1, 3), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [(1, 2), (2, 1), (4, 1)], merge_max_comp) ==
            [(1, 2), (2, 1), (3, 1), (4, 1), (5, 1)];
        assert merge([(1, 1), (3, 1), (5, 1)], [], merge_add_comp) == [(1, 1), (3, 1), (5, 1)];
        assert merge([], [], merge_add_comp) == [];
    }

    #[test]
    fn test_mergei() {
        assert mergei([[], [], []], merge_add_comp) == [];
        assert mergei([[(1, 1), (2, 1)], [(1, 2), (3, 1)], [(-1, 3)]], merge_add_comp) ==
            [(-1, 3), (1, 3), (2, 1), (3, 1)];
        assert mergei([[(1, 1)], [(1, 2)], [(1, 3)]], merge_add_comp) == [(1, 6)];
    }

    #[test]
    fn test_zip_default() {
        assert zip_default([1, 2, 3], [4u, 5u, 6u], (0, 0u)) == [(1, 4u), (2, 5u), (3, 6u)];
        assert zip_default([1, 2, 3], [4u], (0, 0u)) == [(1, 4u), (2, 0u), (3, 0u)];
        assert zip_default([], [], (0, 0u)) == [];
    }

    #[test]
    fn test_div_rem() {
        let (d, m) = div_rem(1234u, 56u);
        assert d * 56u + m == 1234u;
    }
}
<|endoftext|>


<commit_before>//! Prepared statements cache for faster execution.
extern crate lru_cache;

use {SqliteResult, SqliteConnection, SqliteStatement};
use self::lru_cache::LruCache;

/// Prepared statements cache.
///
/// FIXME limitation: the same SQL can be cached only once...
#[derive(Debug)]
pub struct StatementCache<'conn> {
    pub conn: &'conn SqliteConnection,
    cache: LruCache<String, SqliteStatement<'conn>>,
}

impl<'conn> StatementCache<'conn> {
    /// Create a statement cache.
    pub fn new(conn: &'conn SqliteConnection, capacity: usize) -> StatementCache<'conn> {
        StatementCache{ conn: conn,  cache: LruCache::new(capacity) }
    }

    /// Search the cache for a prepared-statement object that implements `sql`.
    // If no such prepared-statement can be found, allocate and prepare a new one.
    pub fn get(&mut self, sql: &str) -> SqliteResult<SqliteStatement<'conn>> {
        let stmt = self.cache.remove(sql);
        match stmt {
            Some(stmt) => Ok(stmt),
            _ => self.conn.prepare(sql)
        }
    }

    /// If `discard` is true, then the statement is deleted immediately.
    /// Otherwise it is added to the LRU list and may be returned
    /// by a subsequent call to `get()`.
    pub fn release(&mut self, stmt: SqliteStatement<'conn>, discard: bool) -> SqliteResult<()> {
        if discard {
            return stmt.finalize();
        }
        // FIXME stmt.reset_if_needed();
        // clear bindings ???
        self.cache.insert(stmt.sql(), stmt).map_or(Ok(()), |stmt| stmt.finalize())
    }

    /// Flush the prepared statement cache
    pub fn flush(&mut self) {
        self.cache.clear();
    }

    /// Return (current, max) sizes.
    pub fn size(&self) -> (usize, usize) {
        (self.cache.len(), self.cache.capacity())
    }

    /// Set the maximum number of cached statements.
    pub fn set_size(&mut self, capacity: usize) {
        self.cache.set_capacity(capacity);
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;
    use super::StatementCache;

   #[test]
    fn test_cache() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let mut cache = StatementCache::new(&db, 10);
        let sql = "PRAGMA schema_version";
        let mut stmt = cache.get(sql).unwrap();
        //println!("NEW {:?}", stmt);
        cache.release(stmt, false).unwrap();
        stmt = cache.get(sql).unwrap();
        //println!("CACHED {:?}", stmt);
        cache.release(stmt, true).unwrap();
        cache.flush();
    }
}
<commit_msg>Clean use statement.<commit_after>//! Prepared statements cache for faster execution.
extern crate lru_cache;

use {SqliteResult, SqliteConnection, SqliteStatement};
use lru_cache::LruCache;

/// Prepared statements cache.
///
/// FIXME limitation: the same SQL can be cached only once...
#[derive(Debug)]
pub struct StatementCache<'conn> {
    pub conn: &'conn SqliteConnection,
    cache: LruCache<String, SqliteStatement<'conn>>,
}

impl<'conn> StatementCache<'conn> {
    /// Create a statement cache.
    pub fn new(conn: &'conn SqliteConnection, capacity: usize) -> StatementCache<'conn> {
        StatementCache{ conn: conn,  cache: LruCache::new(capacity) }
    }

    /// Search the cache for a prepared-statement object that implements `sql`.
    // If no such prepared-statement can be found, allocate and prepare a new one.
    pub fn get(&mut self, sql: &str) -> SqliteResult<SqliteStatement<'conn>> {
        let stmt = self.cache.remove(sql);
        match stmt {
            Some(stmt) => Ok(stmt),
            _ => self.conn.prepare(sql)
        }
    }

    /// If `discard` is true, then the statement is deleted immediately.
    /// Otherwise it is added to the LRU list and may be returned
    /// by a subsequent call to `get()`.
    pub fn release(&mut self, stmt: SqliteStatement<'conn>, discard: bool) -> SqliteResult<()> {
        if discard {
            return stmt.finalize();
        }
        // FIXME stmt.reset_if_needed();
        // clear bindings ???
        self.cache.insert(stmt.sql(), stmt).map_or(Ok(()), |stmt| stmt.finalize())
    }

    /// Flush the prepared statement cache
    pub fn flush(&mut self) {
        self.cache.clear();
    }

    /// Return (current, max) sizes.
    pub fn size(&self) -> (usize, usize) {
        (self.cache.len(), self.cache.capacity())
    }

    /// Set the maximum number of cached statements.
    pub fn set_size(&mut self, capacity: usize) {
        self.cache.set_capacity(capacity);
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;
    use super::StatementCache;

   #[test]
    fn test_cache() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let mut cache = StatementCache::new(&db, 10);
        let sql = "PRAGMA schema_version";
        let mut stmt = cache.get(sql).unwrap();
        //println!("NEW {:?}", stmt);
        cache.release(stmt, false).unwrap();
        stmt = cache.get(sql).unwrap();
        //println!("CACHED {:?}", stmt);
        cache.release(stmt, true).unwrap();
        cache.flush();
    }
}
<|endoftext|>


<commit_before>use super::prelude::*;

/// Returns the JSON representation of the current deployed commit sha.
///
/// The sha is contained within the `HEROKU_SLUG_COMMIT` environment variable.
/// If `HEROKU_SLUG_COMMIT` is not set, returns `"unknown"`.
pub fn show_deployed_sha(req: &mut dyn RequestExt) -> EndpointResult {
    let deployed_sha =
        dotenv::var("HEROKU_SLUG_COMMIT").unwrap_or_else(|_| String::from("unknown"));

    Ok(req.json(&json!({
        "deployed_sha": &deployed_sha[..],
        "commit": &deployed_sha[..],
    })))
}
<commit_msg>Auto merge of #3994 - Turbo87:read-only, r=locks<commit_after>use super::prelude::*;

/// Returns the JSON representation of the current deployed commit sha.
///
/// The sha is contained within the `HEROKU_SLUG_COMMIT` environment variable.
/// If `HEROKU_SLUG_COMMIT` is not set, returns `"unknown"`.
pub fn show_deployed_sha(req: &mut dyn RequestExt) -> EndpointResult {
    let config = &req.app().config;
    let read_only = config.db.are_all_read_only();

    let deployed_sha =
        dotenv::var("HEROKU_SLUG_COMMIT").unwrap_or_else(|_| String::from("unknown"));

    Ok(req.json(&json!({
        "deployed_sha": &deployed_sha[..],
        "commit": &deployed_sha[..],
        "read_only": read_only,
    })))
}
<|endoftext|>


<commit_before>const DATA: &'static str = include_str!("../data/input_17.txt");

pub fn main() -> Vec<String> {
    vec![part1().to_string(), part2().to_string()]
}

fn part1() -> usize {
    let mut data: Vec<u32> = DATA.lines().map(|l| l.parse::<u32>().unwrap()).collect();
    data.sort();
    let ps = PowerSet::with_prune_condition(&data, |ss| ss.iter().sum::<u32>() > 150);
    let v = ps.filter(|ss| ss.iter().sum::<u32>() == 150).collect::<Vec<_>>();
    v.len()
}

fn part2() -> usize {
    let mut data: Vec<u32> = DATA.lines().map(|l| l.parse::<u32>().unwrap()).collect();
    data.sort();
    let ps = PowerSet::with_prune_condition(&data, |ss| ss.iter().sum::<u32>() > 150);
    let v = ps.filter(|ss| ss.iter().sum::<u32>() == 150).collect::<Vec<_>>();
    let min = v.iter().map(|ss| ss.len()).min().unwrap();
    v.iter().filter(|ss| ss.len() == min).count()
}

struct PowerSet<T, F>
    where T: Copy,
          F: Fn(&[T]) -> bool
{
    stack: Vec<(Vec<T>, Vec<T>)>,
    stop: F,
}

impl<T, F> PowerSet<T, F>
    where T: Copy,
          F: Fn(&[T]) -> bool
{
    // not sure why this doesn't compile
    // expected type parameter, found fn item
    //
    //  fn new(data: &[T]) -> PowerSet<T, F> {
    //      PowerSet::with_prune_condition(data, |_| false)
    //  }

    fn with_prune_condition(data: &[T], stop: F) -> PowerSet<T, F> {
        let mut stack = Vec::new();
        if let Some((selected, remaining)) = data.split_last() {
            stack.push((vec![*selected], remaining.to_vec()));
            stack.push((vec![], remaining.to_vec()));
        } else {
            stack.push((vec![], vec![]));
        }
        PowerSet {
            stack: stack,
            stop: stop,
        }
    }
}

impl<T, F> Iterator for PowerSet<T, F>
    where T: Copy,
          F: Fn(&[T]) -> bool
{
    type Item = Vec<T>;

    fn next(&mut self) -> Option<Vec<T>> {
        if let Some((selected, remaining)) = self.stack.pop() {
            if let Some((sel, rem)) = remaining.split_last() {
                let mut r = selected.clone();
                r.push(*sel);
                if !(self.stop)(&selected) {
                    self.stack.push((r, rem.to_vec()));
                    self.stack.push((selected.clone(), rem.to_vec()));
                }
                self.next()
            } else {
                Some(selected)
            }
        } else {
            None
        }
    }
}
<commit_msg>day 17: just track slices of remaining input data instead of copying out to vec<commit_after>const DATA: &'static str = include_str!("../data/input_17.txt");

pub fn main() -> Vec<String> {
    vec![part1().to_string(), part2().to_string()]
}

fn part1() -> usize {
    let mut data: Vec<u32> = DATA.lines().map(|l| l.parse::<u32>().unwrap()).collect();
    data.sort();
    let ps = PowerSet::with_prune_condition(&data, |ss| ss.iter().sum::<u32>() > 150);
    let v = ps.filter(|ss| ss.iter().sum::<u32>() == 150).collect::<Vec<_>>();
    v.len()
}

fn part2() -> usize {
    let mut data: Vec<u32> = DATA.lines().map(|l| l.parse::<u32>().unwrap()).collect();
    data.sort();
    let ps = PowerSet::with_prune_condition(&data, |ss| ss.iter().sum::<u32>() > 150);
    let v = ps.filter(|ss| ss.iter().sum::<u32>() == 150).collect::<Vec<_>>();
    let min = v.iter().map(|ss| ss.len()).min().unwrap();
    v.iter().filter(|ss| ss.len() == min).count()
}

struct PowerSet<'a, T, F>
    where T: 'a + Copy,
          F: Fn(&[T]) -> bool
{
    stack: Vec<(Vec<T>, &'a [T])>,
    stop: F,
}

impl<'a, T, F> PowerSet<'a, T, F>
    where T: 'a + Copy,
          F: Fn(&[T]) -> bool
{
    // not sure why this doesn't compile
    // expected type parameter, found fn item
    //
    //  fn new(data: &'a [T]) -> PowerSet<'a, T, F> {
    //      PowerSet::with_prune_condition(data, |_| false)
    //  }

    fn with_prune_condition(data: &'a [T], stop: F) -> PowerSet<'a, T, F> {
        let mut stack = Vec::new();
        if let Some((selected, remaining)) = data.split_last() {
            stack.push((vec![*selected], remaining));
            stack.push((vec![], remaining));
        } else {
            // stack.push((vec![], &[T::zero(); 0][..]))
            stack.push((vec![], data))
        }
        PowerSet {
            stack: stack,
            stop: stop,
        }
    }
}

impl<'a, T, F> Iterator for PowerSet<'a, T, F>
    where T: 'a + Copy,
          F: Fn(&[T]) -> bool
{
    type Item = Vec<T>;

    fn next(&mut self) -> Option<Vec<T>> {
        if let Some((selected, remaining)) = self.stack.pop() {
            if let Some((sel, rem)) = remaining.split_last() {
                if !(self.stop)(&selected) {
                    let mut r = selected.clone();
                    r.push(*sel);
                    self.stack.push((r, rem));
                    self.stack.push((selected, rem));
                }
                self.next()
            } else {
                Some(selected)
            }
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>// mod error
use std::error::{self, Error as Err};
use std::fmt::{self, Display};
use std::io;
use std::convert::From;
use std::net::AddrParseError;

/// # Example
/// ```
/// use sqlib::SQError;
///
/// let line = "error id=0 msg=ok".to_string();
///
/// let err_option = SQError::parse(&line);
/// let err = match err_option {
///     Some(err) => err,
///     None => { panic!("no error found"); },
/// };
/// assert_eq!(0, err.id());
/// ```
#[derive(Debug)]
pub struct SQError {
    id: u32,
    msg: String,
    full_msg: String,
}

// helping function for SQError::parse
fn is_seperator(c: char) -> bool {
    c == '=' || c.is_whitespace()
}

impl SQError {
    pub fn new(id: u32, msg: String) -> SQError {
        let full_msg_str = format!("error id={} msg={}", id, &msg);
        SQError {
            id: id,
            msg: msg,
            full_msg: full_msg_str,
        }
    }

    pub fn ok() -> SQError {
        SQError::new(0, "ok".to_string())
    }

    pub fn parse_is_ok(s: &str) -> super::Result<bool> {
        let err = match SQError::parse(s) {
            None => {
                return Ok(false);
            }
            Some(err) => err,
        };
        if err == SQError::ok() {
            Ok(true)
        } else {
            Err(Error::from(err))
        }
    }

    /// try to parse a String to a SQError
    pub fn parse(s: &str) -> Option<SQError> {
        // the str shouldn't be trimmed, because a real error is without
        // whitespace in the beginning
        let parts: Vec<&str> = s.splitn(6, is_seperator).collect();
        if parts.len() < 5 {
            return None;
        }
        if parts[0] != "error" {
            return None;
        }
        if parts[1] != "id" {
            return None;
        }
        let id_result = parts[2].clone().parse::<u32>();
        let id = match id_result {
            Err(_) => {
                return None;
            }
            Ok(val) => val,
        };
        if parts[3] != "msg" {
            return None;
        }
        let msg = parts[4].to_string().clone();
        Some(SQError::new(id, msg))
    }

    pub fn id(&self) -> u32 {
        self.id
    }

    pub fn msg(&self) -> String {
        self.msg.clone()
    }
}

impl PartialEq for SQError {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for SQError {}

impl Display for SQError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.full_msg)
    }
}

impl error::Error for SQError {
    fn description(&self) -> &str {
        &self.full_msg
    }
}

/// SQError is a custom Error type for the sqlib.
#[derive(Debug)]
pub enum Error {
    /// wraps io::Error
    Io(io::Error),
    /// server query error messages
    SQ(SQError),
    /// other errors
    Other(String),
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error::Io(ref err) => err.description(),
            &Error::SQ(ref err) => err.description(),
            &Error::Other(ref s) => s,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl<'a> From<&'a str> for Error {
    fn from(err: &'a str) -> Error {
        Error::Other(err.to_string())
    }
}

impl From<String> for Error {
    fn from(err: String) -> Error {
        Error::Other(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<SQError> for Error {
    fn from(err: SQError) -> Error {
        Error::SQ(err)
    }
}

impl From<AddrParseError> for Error {
    fn from(err: AddrParseError) -> Error {
        Error::Other(err.description().to_string())
    }
}
<commit_msg>unescaping the error message at parsing a String to an Error<commit_after>// mod error
use std::error::{self, Error as Err};
use std::fmt::{self, Display};
use std::io;
use std::convert::From;
use std::net::AddrParseError;
use std::sync::PoisonError;
use escaping::unescape;

/// # Example
/// ```
/// use sqlib::SQError;
///
/// let line = "error id=0 msg=ok".to_string();
///
/// let err_option = SQError::parse(&line);
/// let err = match err_option {
///     Some(err) => err,
///     None => { panic!("no error found"); },
/// };
/// assert_eq!(0, err.id());
/// ```
#[derive(Debug)]
pub struct SQError {
    id: u32,
    msg: String,
    full_msg: String,
}

// helping function for SQError::parse
fn is_seperator(c: char) -> bool {
    c == '=' || c.is_whitespace()
}

impl SQError {
    pub fn new(id: u32, msg: String) -> SQError {
        let full_msg_str = format!("error id={} msg={}", id, &msg);
        SQError {
            id: id,
            msg: msg,
            full_msg: full_msg_str,
        }
    }

    pub fn ok() -> SQError {
        SQError::new(0, "ok".to_string())
    }

    pub fn parse_is_ok(s: &str) -> super::Result<bool> {
        let err = match SQError::parse(s) {
            None => {
                return Ok(false);
            }
            Some(err) => err,
        };
        if err == SQError::ok() {
            Ok(true)
        } else {
            Err(Error::from(err))
        }
    }

    /// try to parse a String to a SQError
    pub fn parse(s: &str) -> Option<SQError> {
        // the str shouldn't be trimmed, because a real error is without
        // whitespace in the beginning
        let parts: Vec<&str> = s.splitn(6, is_seperator).collect();
        if parts.len() < 5 {
            return None;
        }
        if parts[0] != "error" {
            return None;
        }
        if parts[1] != "id" {
            return None;
        }
        let id_result = parts[2].clone().parse::<u32>();
        let id = match id_result {
            Err(_) => {
                return None;
            }
            Ok(val) => val,
        };
        if parts[3] != "msg" {
            return None;
        }
        let msg = parts[4].to_string().clone();
        Some(SQError::new(id, unescape(&msg)))
    }

    pub fn id(&self) -> u32 {
        self.id
    }

    pub fn msg(&self) -> String {
        self.msg.clone()
    }
}

impl PartialEq for SQError {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for SQError {}

impl Display for SQError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.full_msg)
    }
}

impl error::Error for SQError {
    fn description(&self) -> &str {
        &self.full_msg
    }
}

/// SQError is a custom Error type for the sqlib.
#[derive(Debug)]
pub enum Error {
    /// wraps io::Error
    Io(io::Error),
    /// server query error messages
    SQ(SQError),
    /// other errors
    Other(String),
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error::Io(ref err) => err.description(),
            &Error::SQ(ref err) => err.description(),
            &Error::Other(ref s) => s,
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl<'a> From<&'a str> for Error {
    fn from(err: &'a str) -> Error {
        Error::Other(err.to_string())
    }
}

impl From<String> for Error {
    fn from(err: String) -> Error {
        Error::Other(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<SQError> for Error {
    fn from(err: SQError) -> Error {
        Error::SQ(err)
    }
}

impl From<AddrParseError> for Error {
    fn from(err: AddrParseError) -> Error {
        Error::Other(err.description().to_string())
    }
}

impl<T> From<PoisonError<T>> for Error {
    fn from(err: PoisonError<T>) -> Error {
        Error::Other(format!("{}", err))
    }
}
<|endoftext|>