<commit_before>#[cfg(feature = "gen")]
extern crate bindgen;

use std::env;

const LIB_VERSIONS: &[(u8, u8, u8)] = &[(1, 19, 0), (1, 15, 0), (1, 14, 0), (1, 11, 0), (1, 9, 0)];

fn lib_versions() -> impl Iterator<Item = &'static (u8, u8, u8)> {
    LIB_VERSIONS
        .iter()
        .filter(|version| {
            env::var(format!(
                "CARGO_FEATURE_LIBINPUT_{}_{}",
                version.0, version.1
            ))
            .is_ok()
        })
        .chain(Some(&LIB_VERSIONS[LIB_VERSIONS.len() - 1]))
}

#[cfg(not(feature = "gen"))]
fn main() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    if matches!(target_os.as_str(), "linux") {
        println!("cargo:rustc-env=LIBINPUT_TARGET_OS={}", target_os);
    } else {
        panic!(
            "No prebuilt bindings for target os: {}. Try use `gen` feature.",
            target_os
        );
    }

    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    if matches!(target_arch.as_str(), "x86" | "x86_64" | "arm" | "aarch64") {
        println!("cargo:rustc-env=LIBINPUT_TARGET_ARCH={}", target_arch);
    } else {
        panic!(
            "No prebuilt bindings for target arch: {}. Try use `gen` feature.",
            target_arch
        );
    }

    let version = lib_versions().next().unwrap();
    println!(
        "cargo:rustc-env=LIBINPUT_VERSION_STR={}_{}",
        version.0, version.1
    );
}

#[cfg(feature = "gen")]
fn main() {
    use std::path::Path;

    let version = lib_versions().next().unwrap();
    println!(
        "cargo:rustc-env=LIBINPUT_VERSION_STR={}_{}",
        version.0, version.1
    );

    #[cfg(feature = "update_bindings")]
    let dest_dir = Path::new("src")
        .join("platforms")
        .join(env::var("CARGO_CFG_TARGET_OS").unwrap())
        .join(env::var("CARGO_CFG_TARGET_ARCH").unwrap());

    for version in lib_versions() {
        let header = Path::new("include").join(format!(
            "libinput.{}.{}.{}.h",
            version.0, version.1, version.2
        ));

        // Setup bindings builder
        let generated = bindgen::builder()
            .header(header.display().to_string())
            .ctypes_prefix("::libc")
            .whitelist_type(r"^libinput_.*$")
            .whitelist_function(r"^libinput_.*$")
            .generate()
            .unwrap();

        println!("cargo:rerun-if-changed=include/");

        // Generate the bindings
        let out_dir = env::var("OUT_DIR").unwrap();
        let bind_name = format!("gen_{}_{}.rs", version.0, version.1);
        let dest_path = Path::new(&out_dir).join(&bind_name);

        generated.write_to_file(dest_path).unwrap();

        #[cfg(feature = "update_bindings")]
        {
            use std::fs;

            let bind_file = Path::new(&out_dir).join(&bind_name);
            let dest_file = dest_dir.join(&bind_name);

            fs::create_dir_all(&dest_dir).unwrap();
            fs::copy(&bind_file, &dest_file).unwrap();
        }
    }

    #[cfg(feature = "update_bindings")]
    {
        use std::{fs, io::Write};

        if let Ok(github_env) = env::var("GITHUB_ENV") {
            let mut env_file = fs::OpenOptions::new()
                .create(true)
                .append(true)
                .open(github_env)
                .unwrap();
            writeln!(env_file, "INPUT_SYS_BINDINGS_FILE={}", dest_dir.display()).unwrap();
        }
    }
}
<commit_msg>Fix FreeBSD build<commit_after>#[cfg(feature = "gen")]
extern crate bindgen;

use std::env;

const LIB_VERSIONS: &[(u8, u8, u8)] = &[(1, 19, 0), (1, 15, 0), (1, 14, 0), (1, 11, 0), (1, 9, 0)];

fn lib_versions() -> impl Iterator<Item = &'static (u8, u8, u8)> {
    LIB_VERSIONS
        .iter()
        .filter(|version| {
            env::var(format!(
                "CARGO_FEATURE_LIBINPUT_{}_{}",
                version.0, version.1
            ))
            .is_ok()
        })
        .chain(Some(&LIB_VERSIONS[LIB_VERSIONS.len() - 1]))
}

#[cfg(not(feature = "gen"))]
fn main() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    if matches!(target_os.as_str(), "linux") {
        println!("cargo:rustc-env=LIBINPUT_TARGET_OS={}", target_os);
    } else {
        panic!(
            "No prebuilt bindings for target os: {}. Try use `gen` feature.",
            target_os
        );
    }

    let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    if matches!(target_arch.as_str(), "x86" | "x86_64" | "arm" | "aarch64") {
        println!("cargo:rustc-env=LIBINPUT_TARGET_ARCH={}", target_arch);
    } else {
        panic!(
            "No prebuilt bindings for target arch: {}. Try use `gen` feature.",
            target_arch
        );
    }

    let version = lib_versions().next().unwrap();
    println!(
        "cargo:rustc-env=LIBINPUT_VERSION_STR={}_{}",
        version.0, version.1
    );
}

#[cfg(feature = "gen")]
fn main() {
    use std::path::Path;

    let version = lib_versions().next().unwrap();
    println!(
        "cargo:rustc-env=LIBINPUT_VERSION_STR={}_{}",
        version.0, version.1
    );

    #[cfg(feature = "update_bindings")]
    let dest_dir = Path::new("src")
        .join("platforms")
        .join(env::var("CARGO_CFG_TARGET_OS").unwrap())
        .join(env::var("CARGO_CFG_TARGET_ARCH").unwrap());

    for version in lib_versions() {
        let header = Path::new("include").join(format!(
            "libinput.{}.{}.{}.h",
            version.0, version.1, version.2
        ));

        // Setup bindings builder
        let generated = bindgen::builder()
            .clang_arg(match cfg!(target_os = "freebsd") {
                true => "-I/usr/local/include",
                false => "",
            })
            .header(header.display().to_string())
            .ctypes_prefix("::libc")
            .whitelist_type(r"^libinput_.*$")
            .whitelist_function(r"^libinput_.*$")
            .generate()
            .unwrap();

        println!("cargo:rerun-if-changed=include/");

        // Generate the bindings
        let out_dir = env::var("OUT_DIR").unwrap();
        let bind_name = format!("gen_{}_{}.rs", version.0, version.1);
        let dest_path = Path::new(&out_dir).join(&bind_name);

        generated.write_to_file(dest_path).unwrap();

        #[cfg(feature = "update_bindings")]
        {
            use std::fs;

            let bind_file = Path::new(&out_dir).join(&bind_name);
            let dest_file = dest_dir.join(&bind_name);

            fs::create_dir_all(&dest_dir).unwrap();
            fs::copy(&bind_file, &dest_file).unwrap();
        }
    }

    #[cfg(feature = "update_bindings")]
    {
        use std::{fs, io::Write};

        if let Ok(github_env) = env::var("GITHUB_ENV") {
            let mut env_file = fs::OpenOptions::new()
                .create(true)
                .append(true)
                .open(github_env)
                .unwrap();
            writeln!(env_file, "INPUT_SYS_BINDINGS_FILE={}", dest_dir.display()).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use crate::config::{Config, EnvConfig};
use crate::error::{Error, ErrorKind, Result};
use crate::page::parse_page;
use crate::service;
use std::process::{Command, Stdio};

pub fn build_url(cfg: &Config) -> Result<String> {
    let page = parse_page(&cfg)?;
    service::build_page_url(&page, &cfg)
}

fn browse_with_cmd(url: &str, cmd: &str) -> Result<()> {
    let out = Command::new(cmd)
        .arg(url)
        .stdout(Stdio::inherit())
        .output()?;
    if out.status.success() {
        Ok(())
    } else {
        Error::err(ErrorKind::UserBrowseCommandFailed {
            cmd: cmd.to_string(),
            url: url.to_string(),
            msg: String::from_utf8_lossy(&out.stderr)
                .trim()
                .replace('\n', " "),
        })
    }
}

pub fn browse(url: &str, env: &EnvConfig) -> Result<()> {
    if let Some(ref cmd) = env.browse_command {
        return browse_with_cmd(url, cmd);
    }

    match open::that(url) {
        Ok(status) if status.success() => Ok(()),
        Ok(status) => {
            let url = url.to_string();
            let msg = if let Some(code) = status.code() {
                format!("Command exited with non-zero status {}", code)
            } else {
                // TODO: Add signal on target == *nix using ExitStatusExt
                "Command terminated by signal".to_string()
            };
            Error::err(ErrorKind::OpenUrlFailure { url, msg })
        }
        Err(e) => Error::err(ErrorKind::OpenUrlFailure {
            url: url.to_string(),
            msg: format!("{}", e),
        }),
    }
}
<commit_msg>include signal number in error message on command failure if possible<commit_after>use crate::config::{Config, EnvConfig};
use crate::error::{Error, ErrorKind, Result};
use crate::page::parse_page;
use crate::service;
use std::process::{Command, Stdio};

pub fn build_url(cfg: &Config) -> Result<String> {
    let page = parse_page(&cfg)?;
    service::build_page_url(&page, &cfg)
}

fn browse_with_cmd(url: &str, cmd: &str) -> Result<()> {
    let out = Command::new(cmd)
        .arg(url)
        .stdout(Stdio::inherit())
        .output()?;
    if out.status.success() {
        Ok(())
    } else {
        Error::err(ErrorKind::UserBrowseCommandFailed {
            cmd: cmd.to_string(),
            url: url.to_string(),
            msg: String::from_utf8_lossy(&out.stderr)
                .trim()
                .replace('\n', " "),
        })
    }
}

pub fn browse(url: &str, env: &EnvConfig) -> Result<()> {
    if let Some(ref cmd) = env.browse_command {
        return browse_with_cmd(url, cmd);
    }

    match open::that(url) {
        Ok(status) if status.success() => Ok(()),
        Ok(status) => {
            let url = url.to_string();
            let msg = if let Some(code) = status.code() {
                format!("Command exited with non-zero status {}", code)
            } else if cfg!(unix) {
                use std::os::unix::process::ExitStatusExt;
                if let Some(sig) = status.signal() {
                    format!("Command terminated by signal {}", sig)
                } else {
                    "Command terminated by signal".to_string()
                }
            } else {
                "Command terminated by signal".to_string()
            };
            Error::err(ErrorKind::OpenUrlFailure { url, msg })
        }
        Err(e) => Error::err(ErrorKind::OpenUrlFailure {
            url: url.to_string(),
            msg: format!("{}", e),
        }),
    }
}
<|endoftext|>


<commit_before>use clap::ArgMatches;
use std::process::Command;

pub fn open_shell_at_repo(args: &ArgMatches) -> Result<(), (String, i32)> {
    println!("CD to: {}", args.value_of("repository").unwrap());

    match Command::new("zsh").status() {
        Ok(exit_status) => {
            if exit_status.success() {
                Ok(())
            } else {
                Err(("Shell failed".to_string(), exit_status.code().unwrap_or(1)))
            }
        },
        Err(e) => {
            Err((format!("{}", e), 1))
        }
    }
}
<commit_msg>Get shell to run for 'cd' from $SHELL<commit_after>use clap::ArgMatches;
use std::env;
use std::process::Command;

pub fn open_shell_at_repo(args: &ArgMatches) -> Result<(), (String, i32)> {
    info!("CD to: {}", args.value_of("repository").unwrap());

    let shell = match env::var("SHELL") {
        Ok(val) => val,
        Err(e) => return Err((format!("Could not determine shell to use: {}", e), 1)),
    };

    info!("Using shell: {}", shell);
    match Command::new(shell).status() {
        Ok(exit_status) => {
            if exit_status.success() {
                Ok(())
            } else {
                Err(("Shell failed".to_string(), exit_status.code().unwrap_or(1)))
            }
        },
        Err(e) => {
            Err((format!("{}", e), 1))
        }
    }
}
<|endoftext|>


<commit_before>use ffi;
use prob::Prob;

#[deriving(ToStr)]
pub enum VarType {
    Bool(bool),
    Continuous(f64),
    Integer(int)
}

pub enum Bound {
    LE(f64), // <=
    GE(f64)  // >=
}

pub struct Variable {
    name:  ~str,
    vtype: VarType,
    id:    int
}

impl Variable {
    pub fn new(name: ~str, vtype: VarType) -> Variable {
        Variable {
            name:  name,
            vtype: vtype,
            id:    0
        }
    }
}

pub struct Constraint {
    name:  ~str,
    sumof: ~[(f64, @mut Variable)],
    bound: Bound
}

impl Constraint {
    pub fn new(name: ~str, sumof: ~[(f64, @mut Variable)], bound: Bound) -> Constraint {
        Constraint {
            name:  name,
            sumof: sumof,
            bound: bound
        }
    }
}

pub struct Problem {
    name:        ~str,
    constraints: ~[Constraint],
    objective:   ~[(f64, @mut Variable)]
}

impl Problem {
    pub fn new(name: ~str, constraints: ~[Constraint], objective: ~[(f64, @mut Variable)]) -> Problem {
        Problem {
            name:        name,
            constraints: constraints,
            objective:   objective
        }
    }

    pub fn solve(&mut self, maximize: bool) -> f64 {
        do ffi::glp_start {
            let mut lp = Prob::new();

            for c in self.constraints.iter() {
                for &(_, v) in c.sumof.iter() {
                    v.id = -1;
                }
            }

            let mut variables = ~[];
            let mut i = 0;
            for c in self.constraints.iter() {
                for &(_, v) in c.sumof.iter() {
                    if v.id == -1 {
                        i = i + 1;
                        v.id = i;
                        variables.push(v);
                    }
                }
            }

            for &(_, v) in self.objective.iter() {
                if v.id == -1 {
                    i = i + 1;
                    v.id = i;
                    variables.push(v);
                }
            }

            lp.add_rows(self.constraints.len() as i32);
            lp.add_cols(i as i32);

            // variables names and types
            for v in variables.iter() {
                lp.set_col_name(v.id as i32, v.name);
                lp.set_col_bnds(v.id as i32, ffi::GLP_FR, 0.0, 0.0);

                match v.vtype {
                    Bool(_)       => lp.set_col_kind(v.id as i32, ffi::GLP_BV),
                    Continuous(_) => lp.set_col_kind(v.id as i32, ffi::GLP_CV),
                    Integer(_)    => lp.set_col_kind(v.id as i32, ffi::GLP_IV),
                }
            }

            let mut ia = ~[ 0 ];
            let mut ja = ~[ 0 ];
            let mut ar = ~[ 0.0 ];

            // constraints
            for (i, c) in self.constraints.iter().enumerate() {
                lp.set_row_name(i as i32 + 1, c.name);

                match c.bound {
                    GE(val) => lp.set_row_bnds(i as i32 + 1, ffi::GLP_LO, val, 0.0),
                    LE(val) => {
                        lp.set_row_bnds(i as i32 + 1, ffi::GLP_UP, 0.0, val)
                    }
                }

                for &(coeff, v) in c.sumof.iter() {
                    ia.push(i as i32 + 1);
                    ja.push(v.id as i32);
                    ar.push(coeff);
                }
            }

            // objective
            for &(coeff, v) in self.objective.iter() {
                lp.set_obj_coef(v.id as i32, coeff)
            }

            if maximize {
                lp.set_obj_dir(ffi::GLP_MAX)
            }
            else {
                lp.set_obj_dir(ffi::GLP_MIN)
            }

            lp.load_matrix(ia.len() as i32 - 1, ia, ja, ar);

            lp.simplex(None);

            for v in variables.iter() {
                let val = lp.get_col_prim(v.id as i32);
                let newtype =
                    match v.vtype {
                        Bool(_)       => Bool(val != 0.0),
                        Continuous(_) => Continuous(val),
                        Integer(_)    => Integer(val as int)
                    };

                v.vtype = newtype;
            }

            lp.get_obj_val()
        }
    }
}

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

    #[test]
    fn test_small_problem_hl() {
        let x = @mut Variable::new(~"x", Continuous(0.0));
        let y = @mut Variable::new(~"y", Continuous(0.0));

        let c1 = Constraint::new(~"p", ~[(1.0, x), (2.0, y)], LE(1.0));
        let c2 = Constraint::new(~"q", ~[(3.0, x), (1.0, y)], LE(2.0));

        let mut problem = Problem::new(~"Simple problem.", ~[c1, c2], ~[(0.6, x), (0.5, y)]);

        let solution = problem.solve(true);

        println!("z = { }; x = { }; y = { }", solution, x.vtype.to_str(), y.vtype.to_str());
    }
}
<commit_msg>Add comment.<commit_after>use ffi;
use prob::Prob;

#[deriving(ToStr)]
pub enum VarType {
    Bool(bool),
    Continuous(f64),
    Integer(int)
}

pub enum Bound {
    LE(f64), // <=
    GE(f64)  // >=
}

pub struct Variable {
    name:  ~str,
    vtype: VarType,
    id:    int
}

impl Variable {
    pub fn new(name: ~str, vtype: VarType) -> Variable {
        Variable {
            name:  name,
            vtype: vtype,
            id:    0
        }
    }
}

pub struct Constraint {
    name:  ~str,
    sumof: ~[(f64, @mut Variable)],
    bound: Bound
}

impl Constraint {
    pub fn new(name: ~str, sumof: ~[(f64, @mut Variable)], bound: Bound) -> Constraint {
        Constraint {
            name:  name,
            sumof: sumof,
            bound: bound
        }
    }
}

pub struct Problem {
    name:        ~str,
    constraints: ~[Constraint],
    objective:   ~[(f64, @mut Variable)]
}

impl Problem {
    pub fn new(name: ~str, constraints: ~[Constraint], objective: ~[(f64, @mut Variable)]) -> Problem {
        Problem {
            name:        name,
            constraints: constraints,
            objective:   objective
        }
    }

    pub fn solve(&mut self, maximize: bool) -> f64 {
        do ffi::glp_start {
            let mut lp = Prob::new();

            for c in self.constraints.iter() {
                for &(_, v) in c.sumof.iter() {
                    v.id = -1;
                }
            }

            let mut variables = ~[];
            let mut i = 0;
            for c in self.constraints.iter() {
                for &(_, v) in c.sumof.iter() {
                    if v.id == -1 {
                        i = i + 1;
                        v.id = i;
                        variables.push(v);
                    }
                }
            }

            for &(_, v) in self.objective.iter() {
                if v.id == -1 {
                    i = i + 1;
                    v.id = i;
                    variables.push(v);
                }
            }

            lp.add_rows(self.constraints.len() as i32);
            lp.add_cols(i as i32);

            // variables names and types
            for v in variables.iter() {
                lp.set_col_name(v.id as i32, v.name);
                lp.set_col_bnds(v.id as i32, ffi::GLP_FR, 0.0, 0.0);

                match v.vtype {
                    Bool(_)       => lp.set_col_kind(v.id as i32, ffi::GLP_BV),
                    Continuous(_) => lp.set_col_kind(v.id as i32, ffi::GLP_CV),
                    Integer(_)    => lp.set_col_kind(v.id as i32, ffi::GLP_IV),
                }
            }

            let mut ia = ~[ 0 ];
            let mut ja = ~[ 0 ];
            let mut ar = ~[ 0.0 ];

            // constraints
            for (i, c) in self.constraints.iter().enumerate() {
                lp.set_row_name(i as i32 + 1, c.name);

                match c.bound {
                    GE(val) => lp.set_row_bnds(i as i32 + 1, ffi::GLP_LO, val, 0.0),
                    LE(val) => {
                        lp.set_row_bnds(i as i32 + 1, ffi::GLP_UP, 0.0, val)
                    }
                }

                for &(coeff, v) in c.sumof.iter() {
                    ia.push(i as i32 + 1);
                    ja.push(v.id as i32);
                    ar.push(coeff);
                }
            }

            // objective
            for &(coeff, v) in self.objective.iter() {
                lp.set_obj_coef(v.id as i32, coeff)
            }

            if maximize {
                lp.set_obj_dir(ffi::GLP_MAX)
            }
            else {
                lp.set_obj_dir(ffi::GLP_MIN)
            }

            lp.load_matrix(ia.len() as i32 - 1, ia, ja, ar);

            lp.simplex(None);

            for v in variables.iter() {
                let val = lp.get_col_prim(v.id as i32);
                let newtype =
                    match v.vtype {
                        Bool(_)       => Bool(val != 0.0),
                        Continuous(_) => Continuous(val),
                        Integer(_)    => Integer(val as int)
                    };

                v.vtype = newtype;
            }

            lp.get_obj_val()
        }
    }
}

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

    #[test]
    fn test_small_problem_hl() {

        /*
         * Solve:
         *  maximize	0.6 x + 0.5 y
         *  subject to	x     + 2 y <= 1
         *              3 x   + y   <= 2
         */

        let x = @mut Variable::new(~"x", Continuous(0.0));
        let y = @mut Variable::new(~"y", Continuous(0.0));

        let c1 = Constraint::new(~"p", ~[(1.0, x), (2.0, y)], LE(1.0));
        let c2 = Constraint::new(~"q", ~[(3.0, x), (1.0, y)], LE(2.0));

        let mut problem = Problem::new(~"Simple problem.", ~[c1, c2], ~[(0.6, x), (0.5, y)]);

        let solution = problem.solve(true);

        println!("z = { }; x = { }; y = { }", solution, x.vtype.to_str(), y.vtype.to_str());
    }
}
<|endoftext|>


<commit_before>pub mod port {

    pub fn write(port: u16, value: u8) {
        unsafe {
            asm!("outb %al, %dx" :: "{dx}" (port), "{al}" (value) :: "volatile");
        }
    }
}
<commit_msg>Read from port<commit_after>pub mod port {

    pub fn read(port: u16) -> u8 {
        unsafe {
            let byte: u8;
            asm!("inb %dx, %al" : "={al}" (byte) : "{dx}" (port) :: "volatile");
            byte
        }
    }

    pub fn write(port: u16, value: u8) {
        unsafe {
            asm!("outb %al, %dx" :: "{dx}" (port), "{al}" (value) :: "volatile");
        }
    }
}
<|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/.

//! Physical Volumes

use std::str::FromStr;
use std::io::{BufReader, BufRead};
use std::path::{Path, PathBuf};
use std::fs::{File, PathExt};
use std::os::unix::fs::MetadataExt;

use parser::{LvmTextMap, Entry};

/// A struct containing the device's major and minor numbers
///
/// Also allows conversion to/from a single 64bit value.
#[derive(Debug, PartialEq, Clone)]
pub struct Device {
    /// Device major number
    pub major: u32,
    /// Device minor number
    pub minor: u8,
}

impl Device {
    /// Returns the path in `/dev` that corresponds with the device number
    pub fn path(&self) -> Option<PathBuf> {
        let f = File::open("/proc/partitions")
            .ok().expect("Could not open /proc/partitions");

        let reader = BufReader::new(f);

        for line in reader.lines().skip(2) {
            if let Ok(line) = line {
                let spl: Vec<_> = line.split_whitespace().collect();

                if spl[0].parse::<u32>().unwrap() == self.major
                    && spl[1].parse::<u8>().unwrap() == self.minor {
                        return Some(PathBuf::from(format!("/dev/{}", spl[3])));
                    }
            }
        }
        None
    }
}

/// Errors that can occur when converting from a String into a Device
pub enum LvmDeviceError {
    /// IO Error
    IoError,
}

impl FromStr for Device {
    type Err = LvmDeviceError;
    fn from_str(s: &str) -> Result<Device, LvmDeviceError> {
        match s.parse::<i64>() {
            Ok(x) => Ok(Device::from(x as u64)),
            Err(_) => {
                match Path::new(s).metadata() {
                    Ok(x) => Ok(Device::from(x.rdev())),
                    Err(_) => Err(LvmDeviceError::IoError)
                }
            }
        }
    }
}

impl From<u64> for Device {
    fn from(val: u64) -> Device {
        Device { major: (val >> 8) as u32, minor: (val & 0xff) as u8 }
    }
}

impl From<Device> for u64 {
    fn from(dev: Device) -> u64 {
        ((dev.major << 8) ^ (dev.minor as u32 & 0xff)) as u64
    }
}

/// A Physical Volume.
#[derive(Debug, PartialEq, Clone)]
pub struct PV {
    /// The mostly-useless name
    pub name: String,
    /// Its UUID
    pub id: String,
    /// Device number for the block device the PV is on
    pub device: Device,
    /// Status
    pub status: Vec<String>,
    /// Flags
    pub flags: Vec<String>,
    /// The device's size, in bytes
    pub dev_size: u64,
    /// The offset in sectors of where the first extent starts
    pub pe_start: u64,
    /// The number of extents in the PV
    pub pe_count: u64,
}

impl From<PV> for LvmTextMap {
    fn from(pv: PV) -> LvmTextMap {
        let mut map = LvmTextMap::new();

        map.insert("id".to_string(), Entry::String(pv.id));
        let tmp: u64 = pv.device.into();
        map.insert("device".to_string(), Entry::Number(tmp as i64));

        map.insert("status".to_string(),
                   Entry::List(
                       Box::new(
                           pv.status
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("flags".to_string(),
                   Entry::List(
                       Box::new(
                           pv.flags
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("dev_size".to_string(), Entry::Number(pv.dev_size as i64));
        map.insert("pe_start".to_string(), Entry::Number(pv.pe_start as i64));
        map.insert("pe_count".to_string(), Entry::Number(pv.pe_count as i64));

        map
    }
}
<commit_msg>Derive debug for LvmDeviceError<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/.

//! Physical Volumes

use std::str::FromStr;
use std::io::{BufReader, BufRead};
use std::path::{Path, PathBuf};
use std::fs::{File, PathExt};
use std::os::unix::fs::MetadataExt;

use parser::{LvmTextMap, Entry};

/// A struct containing the device's major and minor numbers
///
/// Also allows conversion to/from a single 64bit value.
#[derive(Debug, PartialEq, Clone)]
pub struct Device {
    /// Device major number
    pub major: u32,
    /// Device minor number
    pub minor: u8,
}

impl Device {
    /// Returns the path in `/dev` that corresponds with the device number
    pub fn path(&self) -> Option<PathBuf> {
        let f = File::open("/proc/partitions")
            .ok().expect("Could not open /proc/partitions");

        let reader = BufReader::new(f);

        for line in reader.lines().skip(2) {
            if let Ok(line) = line {
                let spl: Vec<_> = line.split_whitespace().collect();

                if spl[0].parse::<u32>().unwrap() == self.major
                    && spl[1].parse::<u8>().unwrap() == self.minor {
                        return Some(PathBuf::from(format!("/dev/{}", spl[3])));
                    }
            }
        }
        None
    }
}

/// Errors that can occur when converting from a String into a Device
#[derive(Debug)]
pub enum LvmDeviceError {
    /// IO Error
    IoError,
}

impl FromStr for Device {
    type Err = LvmDeviceError;
    fn from_str(s: &str) -> Result<Device, LvmDeviceError> {
        match s.parse::<i64>() {
            Ok(x) => Ok(Device::from(x as u64)),
            Err(_) => {
                match Path::new(s).metadata() {
                    Ok(x) => Ok(Device::from(x.rdev())),
                    Err(_) => Err(LvmDeviceError::IoError)
                }
            }
        }
    }
}

impl From<u64> for Device {
    fn from(val: u64) -> Device {
        Device { major: (val >> 8) as u32, minor: (val & 0xff) as u8 }
    }
}

impl From<Device> for u64 {
    fn from(dev: Device) -> u64 {
        ((dev.major << 8) ^ (dev.minor as u32 & 0xff)) as u64
    }
}

/// A Physical Volume.
#[derive(Debug, PartialEq, Clone)]
pub struct PV {
    /// The mostly-useless name
    pub name: String,
    /// Its UUID
    pub id: String,
    /// Device number for the block device the PV is on
    pub device: Device,
    /// Status
    pub status: Vec<String>,
    /// Flags
    pub flags: Vec<String>,
    /// The device's size, in bytes
    pub dev_size: u64,
    /// The offset in sectors of where the first extent starts
    pub pe_start: u64,
    /// The number of extents in the PV
    pub pe_count: u64,
}

impl From<PV> for LvmTextMap {
    fn from(pv: PV) -> LvmTextMap {
        let mut map = LvmTextMap::new();

        map.insert("id".to_string(), Entry::String(pv.id));
        let tmp: u64 = pv.device.into();
        map.insert("device".to_string(), Entry::Number(tmp as i64));

        map.insert("status".to_string(),
                   Entry::List(
                       Box::new(
                           pv.status
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("flags".to_string(),
                   Entry::List(
                       Box::new(
                           pv.flags
                               .into_iter()
                               .map(|x| Entry::String(x))
                               .collect())));

        map.insert("dev_size".to_string(), Entry::Number(pv.dev_size as i64));
        map.insert("pe_start".to_string(), Entry::Number(pv.pe_start as i64));
        map.insert("pe_count".to_string(), Entry::Number(pv.pe_count as i64));

        map
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # State machine
//!
//! The `StateMachine` trait is implemented by anything that represents a state machine (aka a finite state automaton). These
//! consist of one or more states that are transitioned between upon matching a symbol from the input.
//!
//! State machines in this library can optionally attach output symbols to states. A state with an output symbol is an 'accepting'
//! state: it matches a substring of the output.
//!

use std::rc::*;

///
/// Identifies a state in a state machine
///
pub type StateId = u32;

///
/// Trait implemented by objects that represent a state machine, whose transitions depend on a particular symbol type
///
/// These state machines can be nondeterministic - which is to say, have more than one transition per state. They always
/// start in state 0.
///
pub trait StateMachine<InputSymbol, OutputSymbol> {
    ///
    /// Returns the number of states in this state machine
    ///
    /// Note that if state x exists then state x-1 is also expected to exist provided x > 0. This means that this returns the
    /// first unused state in this state machine.
    ///
    fn count_states(&self) -> StateId;

    ///
    /// Returns the transitions for a particular symbol
    ///
    fn get_transitions_for_state(&self, state: StateId) -> Vec<(InputSymbol, StateId)>;

    ///
    /// If a state is an accepting state, then this returns the output symbol that should be produced if this is the longest match
    ///
    fn output_symbol_for_state(&self, state: StateId) -> Option<&OutputSymbol>;
}

///
/// Trait implemented by state machines that can be altered
///
pub trait MutableStateMachine<InputSymbol, OutputSymbol> : StateMachine<InputSymbol, OutputSymbol> {
    ///
    /// Adds a transition from a particular state to another on seeing a symbol
    ///
    fn add_transition(&mut self, state: StateId, for_symbol: InputSymbol, new_state: StateId);

    ///
    /// Ensures that a state with the specified ID exists in this state machine
    ///
    fn create_state(&mut self, state: StateId);

    ///
    /// Sets the output symbol to use for a particular state
    ///
    fn set_output_symbol(&mut self, state: StateId, new_output_symbol: OutputSymbol);

    ///
    /// Joins two states in this state machine
    ///
    /// This means that `first_state` will follow the transitions for `second_state` - that is, any transition that appears
    /// in `second_state` will also appear in `first_state` - including transitions that are added after this call.
    ///
    /// The reverse is not true: `second_state` does not acquire the transitions from `first_state`.
    ///
    /// This could be considered as creating an empty or 'epsilon' transition between the first state and the second state,
    /// which is useful for building NDFAs from regular languages.
    ///
    /// These semantics mean that callers don't have to treat empty transitions as special cases, and also ensure that state
    /// 0 is always the sole start state for the automaton.
    ///
    fn join_states(&mut self, first_state: StateId, second_state: StateId);
}

///
/// Trait used to indicate that a particular state machine is deterministic (has at most one
/// transition per symbol from the original)
///
pub trait DeterministicStateMachine<InputSymbol, OutputSymbol> : StateMachine<InputSymbol, OutputSymbol> { }

///
/// Trait implemented by things that can be converted into a non-deterministic state machine with a specific symbol for
/// the output state.
///
pub trait ToNdfa<InputSymbol> {
    fn to_ndfa<OutputSymbol>(&self, output: OutputSymbol) -> Box<StateMachine<InputSymbol, OutputSymbol>>;
}

///
/// Any reference to a state machine is also a state machine
///
impl<InputSymbol, OutputSymbol> StateMachine<InputSymbol, OutputSymbol> for Rc<StateMachine<InputSymbol, OutputSymbol>> {
    #[inline]
    fn count_states(&self) -> StateId {
        (**self).count_states()
    }

    #[inline]
    fn get_transitions_for_state(&self, state: StateId) -> Vec<(InputSymbol, StateId)> {
        (**self).get_transitions_for_state(state)
    }

    #[inline]
    fn output_symbol_for_state(&self, state: StateId) -> Option<&OutputSymbol> {
        (**self).output_symbol_for_state(state)
    }
}

impl<InputSymbol, OutputSymbol> StateMachine<InputSymbol, OutputSymbol> for Rc<DeterministicStateMachine<InputSymbol, OutputSymbol>> {
    #[inline]
    fn count_states(&self) -> StateId {
        (**self).count_states()
    }

    #[inline]
    fn get_transitions_for_state(&self, state: StateId) -> Vec<(InputSymbol, StateId)> {
        (**self).get_transitions_for_state(state)
    }

    #[inline]
    fn output_symbol_for_state(&self, state: StateId) -> Option<&OutputSymbol> {
        (**self).output_symbol_for_state(state)
    }
}

impl<InputSymbol, OutputSymbol> DeterministicStateMachine<InputSymbol, OutputSymbol> for Rc<DeterministicStateMachine<InputSymbol, OutputSymbol>> {
}
<commit_msg>Don’t need the DeterministicStateMachine trait any more<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # State machine
//!
//! The `StateMachine` trait is implemented by anything that represents a state machine (aka a finite state automaton). These
//! consist of one or more states that are transitioned between upon matching a symbol from the input.
//!
//! State machines in this library can optionally attach output symbols to states. A state with an output symbol is an 'accepting'
//! state: it matches a substring of the output.
//!

use std::rc::*;

///
/// Identifies a state in a state machine
///
pub type StateId = u32;

///
/// Trait implemented by objects that represent a state machine, whose transitions depend on a particular symbol type
///
/// These state machines can be nondeterministic - which is to say, have more than one transition per state. They always
/// start in state 0.
///
pub trait StateMachine<InputSymbol, OutputSymbol> {
    ///
    /// Returns the number of states in this state machine
    ///
    /// Note that if state x exists then state x-1 is also expected to exist provided x > 0. This means that this returns the
    /// first unused state in this state machine.
    ///
    fn count_states(&self) -> StateId;

    ///
    /// Returns the transitions for a particular symbol
    ///
    fn get_transitions_for_state(&self, state: StateId) -> Vec<(InputSymbol, StateId)>;

    ///
    /// If a state is an accepting state, then this returns the output symbol that should be produced if this is the longest match
    ///
    fn output_symbol_for_state(&self, state: StateId) -> Option<&OutputSymbol>;
}

///
/// Trait implemented by state machines that can be altered
///
pub trait MutableStateMachine<InputSymbol, OutputSymbol> : StateMachine<InputSymbol, OutputSymbol> {
    ///
    /// Adds a transition from a particular state to another on seeing a symbol
    ///
    fn add_transition(&mut self, state: StateId, for_symbol: InputSymbol, new_state: StateId);

    ///
    /// Ensures that a state with the specified ID exists in this state machine
    ///
    fn create_state(&mut self, state: StateId);

    ///
    /// Sets the output symbol to use for a particular state
    ///
    fn set_output_symbol(&mut self, state: StateId, new_output_symbol: OutputSymbol);

    ///
    /// Joins two states in this state machine
    ///
    /// This means that `first_state` will follow the transitions for `second_state` - that is, any transition that appears
    /// in `second_state` will also appear in `first_state` - including transitions that are added after this call.
    ///
    /// The reverse is not true: `second_state` does not acquire the transitions from `first_state`.
    ///
    /// This could be considered as creating an empty or 'epsilon' transition between the first state and the second state,
    /// which is useful for building NDFAs from regular languages.
    ///
    /// These semantics mean that callers don't have to treat empty transitions as special cases, and also ensure that state
    /// 0 is always the sole start state for the automaton.
    ///
    fn join_states(&mut self, first_state: StateId, second_state: StateId);
}

///
/// Trait implemented by things that can be converted into a non-deterministic state machine with a specific symbol for
/// the output state.
///
pub trait ToNdfa<InputSymbol> {
    fn to_ndfa<OutputSymbol>(&self, output: OutputSymbol) -> Box<StateMachine<InputSymbol, OutputSymbol>>;
}

///
/// Any reference to a state machine is also a state machine
///
impl<InputSymbol, OutputSymbol> StateMachine<InputSymbol, OutputSymbol> for Rc<StateMachine<InputSymbol, OutputSymbol>> {
    #[inline]
    fn count_states(&self) -> StateId {
        (**self).count_states()
    }

    #[inline]
    fn get_transitions_for_state(&self, state: StateId) -> Vec<(InputSymbol, StateId)> {
        (**self).get_transitions_for_state(state)
    }

    #[inline]
    fn output_symbol_for_state(&self, state: StateId) -> Option<&OutputSymbol> {
        (**self).output_symbol_for_state(state)
    }
}
<|endoftext|>


<commit_before>pub mod functions;

use arrayvec::ArrayVec;
use std::io;
use std::path::Path;
use std::borrow::Cow;
pub use self::functions::*;

#[derive(Debug)]
pub enum TokenErr {
    File(io::Error),
    OutOfBounds,
}

#[derive(Clone, PartialEq, Debug)]
/// A token is a placeholder for the operation to be performed on the input value.
pub enum Token<'a> {
    /// An argument is simply a collection of characters that are not placeholders.
    Argument(Cow<'a, str>),
    /// Takes the basename (file name) of the input with the extension removed.
    BaseAndExt,
    /// Takes the basename (file name) of the input with the directory path removed.
    Basename,
    /// Takes the directory path of the input with the basename removed.
    Dirname,
    /// Returns the job ID of the current input.
    Job,
    /// Takes the input, unmodified.
    Placeholder,
    /// Removes the extension from the input.
    RemoveExtension,
    /// Returns the thread ID.
    Slot
}

struct Number<'a> {
    id: usize,
    token: Token<'a>,
}

impl<'a> Number<'a> {
    fn new(id: usize, token: Token<'a>) -> Number<'a> {
        Number{ id: id, token: token }
    }

    fn into_argument(self, path: &Path) -> Result<String, TokenErr> {
        use std::fs::File;
        use std::io::{BufRead, BufReader};
        let file = File::open(path).map_err(TokenErr::File)?;
        let input = &BufReader::new(file).lines().nth(self.id-1).unwrap().map_err(TokenErr::File)?;
        let argument = match self.token {
            Token::Argument(_)     => unreachable!(),
            Token::Basename        => basename(input),
            Token::BaseAndExt      => basename(remove_extension(input)),
            Token::Dirname         => dirname(input),
            Token::Job             => unreachable!(),
            Token::Placeholder     => input,
            Token::RemoveExtension => remove_extension(input),
            Token::Slot            => unreachable!()
        };
        Ok(String::from(argument))
    }
}

/// Takes the command arguments as the input and reduces it into tokens,
/// which allows for easier management of string manipulation later on.
pub fn tokenize<'a>(tokens: &mut ArrayVec<[Token<'a>; 128]>, template: &'a str, path: &Path, nargs: usize)
    -> Result<(), TokenErr>
{
    // When set to true, the characters following will be collected into `pattern`.
    let mut pattern_matching = false;
    // Mark the index where the pattern's first character begins.
    let mut pattern_start = 0;

    // Defines that an argument string is currently being matched
    let mut argument_matching = false;
    // Mark the index where the argument's first character begins.
    let mut argument_start = 0;

    for (id, character) in template.chars().enumerate() {
        match (character, pattern_matching) {
            // This condition initiates the pattern matching
            ('{', false) => {
                pattern_matching = true;
                pattern_start    = id;

                // If pattern matching has initialized while argument matching was happening,
                // this will append the argument to the token list and disable argument matching.
                if argument_matching {
                    argument_matching = false;
                    let argument      = Cow::Borrowed(&template[argument_start..id]);
                    tokens.push(Token::Argument(argument));
                }
            },
            // This condition ends the pattern matching process
            ('}', true)  => {
                pattern_matching = false;
                if id == pattern_start+1 {
                    // This condition will be met when the pattern is "{}".
                    tokens.push(Token::Placeholder);
                } else {
                    // Supply the internal contents of the pattern to the token matcher.
                    match match_token(&template[pattern_start+1..id], path, nargs)? {
                        // If the token is a match, add the matched token.
                        Some(token) => { tokens.push(token).unwrap(); },
                        // If the token is not a match, add it as an argument.
                        None => { tokens.push(Token::Argument(Cow::Borrowed(&template[pattern_start..id+1]))); }
                    }
                }
            },
            // If pattern matching is disabled and argument matching is also disabled,
            // this will begin the argument matching process.
            (_, false) if !argument_matching  => {
                argument_matching = true;
                argument_start    = id;
            },
            (_, _) => ()
        }
    }

    // In the event that there is leftover data that was not matched, this will add the final
    // string to the token list.
    if pattern_matching {
        tokens.push(Token::Argument(Cow::Borrowed(&template[pattern_start..])));
    } else if argument_matching {
        tokens.push(Token::Argument(Cow::Borrowed(&template[argument_start..])));
    }

    Ok(())
}

/// Matches a pattern to it's associated token.
fn match_token<'a>(pattern: &'a str, path: &Path, nargs: usize) -> Result<Option<Token<'a>>, TokenErr> {
    match pattern {
        "."  => Ok(Some(Token::RemoveExtension)),
        "#"  => Ok(Some(Token::Job)),
        "%"  => Ok(Some(Token::Slot)),
        "/"  => Ok(Some(Token::Basename)),
        "//" => Ok(Some(Token::Dirname)),
        "/." => Ok(Some(Token::BaseAndExt)),
        "#^" => Ok(Some(Token::Argument(Cow::Owned(nargs.to_string())))),
        _    => {
            let ndigits = pattern.chars().take_while(|&x| x.is_numeric()).count();
            let nchars  = ndigits + pattern.chars().skip(ndigits).count();
            if ndigits != 0 {
                let number = pattern[0..ndigits].parse::<usize>().unwrap();
                if ndigits == nchars {
                    if number == 0 || number > nargs { return Err(TokenErr::OutOfBounds); }
                    let argument = Number::new(number, Token::Placeholder).into_argument(path)?;
                    Ok(Some(Token::Argument(Cow::Owned(argument))))
                } else {
                    match match_token(&pattern[ndigits..], path, nargs)? {
                        None | Some(Token::Job) |  Some(Token::Slot) => Ok(None),
                        Some(token) => {
                            let argument = Number::new(number, token).into_argument(path)?;
                            Ok(Some(Token::Argument(Cow::Owned(argument))))
                        },
                    }
                }
            } else {
                Ok(None)
            }
        }
    }
}

#[test]
fn tokenizer_argument() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "foo", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Argument("foo".to_owned())]);
}

#[test]
fn tokenizer_placeholder() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Placeholder]);
}

#[test]
fn tokenizer_remove_extension() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{.}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::RemoveExtension]);
}

#[test]
fn tokenizer_basename() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{/}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Basename]);
}

#[test]
fn tokenizer_dirname() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{//}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Dirname]);
}

#[test]
fn tokenizer_base_and_ext() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{/.}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::BaseAndExt]);
}

#[test]
fn tokenizer_slot() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{%}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Slot]);
}

#[test]
fn tokenizer_job() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{#}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Job]);
}

#[test]
fn tokenizer_multiple() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "foo {} bar", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Argument("foo ".to_owned()), Token::Placeholder,
        Token::Argument(" bar".to_owned())]);
}

#[test]
fn tokenizer_no_space() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "foo{}bar", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Argument("foo".to_owned()), Token::Placeholder,
        Token::Argument("bar".to_owned())]);
}
<commit_msg>Prevent Panic<commit_after>pub mod functions;

use arrayvec::ArrayVec;
use std::io;
use std::path::Path;
use std::borrow::Cow;
pub use self::functions::*;

#[derive(Debug)]
pub enum TokenErr {
    File(io::Error),
    OutOfBounds,
}

#[derive(Clone, PartialEq, Debug)]
/// A token is a placeholder for the operation to be performed on the input value.
pub enum Token<'a> {
    /// An argument is simply a collection of characters that are not placeholders.
    Argument(Cow<'a, str>),
    /// Takes the basename (file name) of the input with the extension removed.
    BaseAndExt,
    /// Takes the basename (file name) of the input with the directory path removed.
    Basename,
    /// Takes the directory path of the input with the basename removed.
    Dirname,
    /// Returns the job ID of the current input.
    Job,
    /// Takes the input, unmodified.
    Placeholder,
    /// Removes the extension from the input.
    RemoveExtension,
    /// Returns the thread ID.
    Slot
}

struct Number<'a> {
    id: usize,
    token: Token<'a>,
}

impl<'a> Number<'a> {
    fn new(id: usize, token: Token<'a>) -> Number<'a> {
        Number{ id: id, token: token }
    }

    fn into_argument(self, path: &Path) -> Result<String, TokenErr> {
        use std::fs::File;
        use std::io::{BufRead, BufReader};
        let file = File::open(path).map_err(TokenErr::File)?;
        let input = &BufReader::new(file).lines().nth(self.id-1).unwrap().map_err(TokenErr::File)?;
        let argument = match self.token {
            Token::Argument(_)     => unreachable!(),
            Token::Basename        => basename(input),
            Token::BaseAndExt      => basename(remove_extension(input)),
            Token::Dirname         => dirname(input),
            Token::Job             => unreachable!(),
            Token::Placeholder     => input,
            Token::RemoveExtension => remove_extension(input),
            Token::Slot            => unreachable!()
        };
        Ok(String::from(argument))
    }
}

/// Takes the command arguments as the input and reduces it into tokens,
/// which allows for easier management of string manipulation later on.
pub fn tokenize<'a>(tokens: &mut ArrayVec<[Token<'a>; 128]>, template: &'a str, path: &Path, nargs: usize)
    -> Result<(), TokenErr>
{
    // When set to true, the characters following will be collected into `pattern`.
    let mut pattern_matching = false;
    // Mark the index where the pattern's first character begins.
    let mut pattern_start = 0;

    // Defines that an argument string is currently being matched
    let mut argument_matching = false;
    // Mark the index where the argument's first character begins.
    let mut argument_start = 0;

    for (id, character) in template.chars().enumerate() {
        match (character, pattern_matching) {
            // This condition initiates the pattern matching
            ('{', false) => {
                pattern_matching = true;
                pattern_start    = id;

                // If pattern matching has initialized while argument matching was happening,
                // this will append the argument to the token list and disable argument matching.
                if argument_matching {
                    argument_matching = false;
                    let argument      = Cow::Borrowed(&template[argument_start..id]);
                    tokens.push(Token::Argument(argument));
                }
            },
            // This condition ends the pattern matching process
            ('}', true)  => {
                pattern_matching = false;
                if id == pattern_start+1 {
                    // This condition will be met when the pattern is "{}".
                    tokens.push(Token::Placeholder);
                } else {
                    // Supply the internal contents of the pattern to the token matcher.
                    match match_token(&template[pattern_start+1..id], path, nargs)? {
                        // If the token is a match, add the matched token.
                        Some(token) => { tokens.push(token); },
                        // If the token is not a match, add it as an argument.
                        None => { tokens.push(Token::Argument(Cow::Borrowed(&template[pattern_start..id+1]))); }
                    }
                }
            },
            // If pattern matching is disabled and argument matching is also disabled,
            // this will begin the argument matching process.
            (_, false) if !argument_matching  => {
                argument_matching = true;
                argument_start    = id;
            },
            (_, _) => ()
        }
    }

    // In the event that there is leftover data that was not matched, this will add the final
    // string to the token list.
    if pattern_matching {
        tokens.push(Token::Argument(Cow::Borrowed(&template[pattern_start..])));
    } else if argument_matching {
        tokens.push(Token::Argument(Cow::Borrowed(&template[argument_start..])));
    }

    Ok(())
}

/// Matches a pattern to it's associated token.
fn match_token<'a>(pattern: &'a str, path: &Path, nargs: usize) -> Result<Option<Token<'a>>, TokenErr> {
    match pattern {
        "."  => Ok(Some(Token::RemoveExtension)),
        "#"  => Ok(Some(Token::Job)),
        "%"  => Ok(Some(Token::Slot)),
        "/"  => Ok(Some(Token::Basename)),
        "//" => Ok(Some(Token::Dirname)),
        "/." => Ok(Some(Token::BaseAndExt)),
        "#^" => Ok(Some(Token::Argument(Cow::Owned(nargs.to_string())))),
        _    => {
            let ndigits = pattern.chars().take_while(|&x| x.is_numeric()).count();
            let nchars  = ndigits + pattern.chars().skip(ndigits).count();
            if ndigits != 0 {
                let number = pattern[0..ndigits].parse::<usize>().unwrap();
                if ndigits == nchars {
                    if number == 0 || number > nargs { return Err(TokenErr::OutOfBounds); }
                    let argument = Number::new(number, Token::Placeholder).into_argument(path)?;
                    Ok(Some(Token::Argument(Cow::Owned(argument))))
                } else {
                    match match_token(&pattern[ndigits..], path, nargs)? {
                        None | Some(Token::Job) |  Some(Token::Slot) => Ok(None),
                        Some(token) => {
                            let argument = Number::new(number, token).into_argument(path)?;
                            Ok(Some(Token::Argument(Cow::Owned(argument))))
                        },
                    }
                }
            } else {
                Ok(None)
            }
        }
    }
}

#[test]
fn tokenizer_argument() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "foo", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Argument("foo".to_owned())]);
}

#[test]
fn tokenizer_placeholder() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Placeholder]);
}

#[test]
fn tokenizer_remove_extension() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{.}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::RemoveExtension]);
}

#[test]
fn tokenizer_basename() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{/}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Basename]);
}

#[test]
fn tokenizer_dirname() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{//}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Dirname]);
}

#[test]
fn tokenizer_base_and_ext() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{/.}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::BaseAndExt]);
}

#[test]
fn tokenizer_slot() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{%}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Slot]);
}

#[test]
fn tokenizer_job() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "{#}", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Job]);
}

#[test]
fn tokenizer_multiple() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "foo {} bar", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Argument("foo ".to_owned()), Token::Placeholder,
        Token::Argument(" bar".to_owned())]);
}

#[test]
fn tokenizer_no_space() {
    let mut tokens = Vec::new();
    let _ = tokenize(&mut tokens, "foo{}bar", &Path::new("."), 1);
    assert_eq!(tokens, vec![Token::Argument("foo".to_owned()), Token::Placeholder,
        Token::Argument("bar".to_owned())]);
}
<|endoftext|>


<commit_before>
extern crate rand;
// extern crate time;

// use time::PreciseTime;
use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket};
use file_stream_iter::{FileStreamReader, FileStreamWriter};
use std::io::Error;
use tftp_specific::{ErrorCode, PacketType};
use std::str::FromStr;
use std::net::AddrParseError;
use std::error;
use std::str;
use std::path::Path;

pub struct EndPoint {
    local_connection: SocketAddrV4,
    // A server doesn't need a remote_connection
    remote_connection: Option<SocketAddrV4>,
}

impl EndPoint {
    pub fn new(remote: &str, port: &str, is_server: bool) -> Result<EndPoint, AddrParseError> {

        let ipv4 = try!(get_ip_from(remote));
        if is_server {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 6900),
                remote_connection: None,
            })
        } else {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 9990),
                remote_connection: Some(SocketAddrV4::new(ipv4, 6900)),
            })
        }
    }

    pub fn start_listen(&mut self) -> Result<(), Error> {
        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Start servicing requests from a client
        // Read from the socket
        let mut buf = [0u8; 516]; //UDP packet is 516 bytes
        let (amt, src) = try!(socket.recv_from(&mut buf));
        let mut last_blk_id = 0u16;
        match buf[1] {
            1 => {
                // RRQ
                println!("RRQ received");
                // Get filename to be sent from the packet
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }
                let filename = str::from_utf8(&buf[2..i]);
                println!("file requested: {}", filename.unwrap());
                // Send data via the socket
                let mut fs = FileStreamReader::new(String::from(filename.unwrap())).unwrap();
                let (mut buf, num_bytes_read) = fs.next().unwrap();
                let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                try!(socket.send_to(v.as_slice(), src));
                // TODO start timer here
                loop {
                    // Get ACK
                    let (amt, src) = try!(socket.recv_from(&mut buf));
                    if buf[1] == 4 {
                        println!("ACK received");
                        last_blk_id += 1;
                        let (buf, num_bytes_read) = fs.next().unwrap();
                        let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                        try!(socket.send_to(v.as_slice(), src));
                    }
                }
            },
            2 => {
                //WRQ
                println!("WRQ received");
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }

                //TODO fix the vec use to copy slice from buf
                let mut v = vec![];
                v.copy_from_slice(&buf[2..i]);
                let filename = str::from_utf8(v.as_slice());
                println!("file requested: {}", filename.unwrap());
                let mut writer = FileStreamWriter::new(String::from(filename.unwrap())).unwrap();
                //send ACK
                let mut block_num = 0u16;
                loop{
                    let low = block_num & 0x00FF;
                    let high = (block_num & 0xFF00) >> 8;

                    println!("Sending ACK");
                    socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    Self::clear_buf(&mut buf);
                    let (amt, src) = try!(socket.recv_from(&mut buf));

                    writer.append(&mut buf);
                    if amt - 4 < 512{
                        //TODO do we close writer here?
                        break;
                    }
                    block_num += 1;

                }

            },
            4 => {}
            _ => panic!("unrecognized packet type"),
        }

        Ok(())

    }

    fn create_error_packet(&mut self, error_code: ErrorCode, err_msg : &str)->Vec<u8>{
        let mut v = Vec::with_capacity(2+2+err_msg.len()+1);
        v.push(0);
        v.push(PacketType::ERROR as u8);
        v.push(0);
        v.push(error_code as u8);
        v.extend_from_slice(err_msg.as_bytes());
        v.push('\0' as u8);
        v
    }

    fn clear_buf(buf : &mut[u8]){
        assert!(buf.len() == 516);
        for i in 0..516{
            buf[i] = 0;
        }
    }

    fn create_data_packet(&mut self, blk_id: u16, buf: &[u8], num_bytes: usize) -> Vec<u8> {
        let mut v = Vec::with_capacity(num_bytes + 4);
        v.push(0);
        v.push(PacketType::DATA as u8);
        v.push((blk_id >> 8) as u8);
        v.push((blk_id & 0x00FF) as u8);
        v.extend_from_slice(&buf[0..num_bytes]);
        v
    }

    fn create_rrq_wrq_packet(&self,
                             p_type: PacketType,
                             file_name: &str,
                             mode: &'static str)
                             -> Vec<u8> {
        let mut v = Vec::with_capacity(2 + file_name.len() + 1 + mode.len() + 1);
        v.push(0);
        v.push(p_type as u8);
        v.extend(file_name.as_bytes());
        v.push('\0' as u8);
        v.push(0);//zero terminator
        v.extend(mode.as_bytes());
        v.push(0);//zero terminator
        v
    }

    pub fn get(&mut self, files: &[&str], mode: &'static str) -> Result<(), Error> {
        let socket = try!(UdpSocket::bind(self.local_connection));
        let mut buf = [0; 516];
        for file in files {
            // start with a RRQ
            println!("sending RRQ request");
            let packet = self.create_rrq_wrq_packet(PacketType::RRQ, file, mode);
            try!(socket.send_to(packet.as_slice(), self.remote_connection.unwrap()));
            // TODO remove the hardcoded filename
            let path = Path::new(file);

            println!("Downloading as ./{}", path.file_name().unwrap().to_str().unwrap());
            //TODO fix downloading very file in the current dir
            let mut writer =
                FileStreamWriter::new(format!("./{}", path.file_name().unwrap().to_str().unwrap()))
                    .unwrap();

            loop {
                // get the first block of the requested file
                let (amt, src) = try!(socket.recv_from(&mut buf));
                println!("Received block");
                println!("Total bytes read {}", amt);

                match buf[1] {
                    3 => {
                        let block_size = amt - 4;
                        println!("File block size is {:?}", block_size);
                        if block_size == 0 {
                            println!("Empty block recvd");
                            writer.close();
                            break;
                        }
                        println!("Appending to file");
                        writer.append(&buf[4..4 + block_size]);
                        let block_num: u16 = 0u16 | (buf[2] as u16) << 8 | buf[3] as u16;

                        // send ACK
                        let low = block_num & 0x00FF;
                        let high = (block_num & 0xFF00) >> 8;

                        println!("Sending ACK");
                        socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    }
                    _ => {}
                }
                // Flush buf for next read
                // TODO fix this:
                for i in 0..516 {
                    buf[0] = 0;
                }
            }

        }

        Ok(())
    }
}

fn get_ip_from(ip: &str) -> Result<Ipv4Addr, AddrParseError> {
    Ipv4Addr::from_str(ip)
}
<commit_msg>Use null terminator instead of 0<commit_after>
extern crate rand;
// extern crate time;

// use time::PreciseTime;
use std::net::{Ipv4Addr, SocketAddrV4, UdpSocket};
use file_stream_iter::{FileStreamReader, FileStreamWriter};
use std::io::Error;
use tftp_specific::{ErrorCode, PacketType};
use std::str::FromStr;
use std::net::AddrParseError;
use std::error;
use std::str;
use std::path::Path;

pub struct EndPoint {
    local_connection: SocketAddrV4,
    // A server doesn't need a remote_connection
    remote_connection: Option<SocketAddrV4>,
}

impl EndPoint {
    pub fn new(remote: &str, port: &str, is_server: bool) -> Result<EndPoint, AddrParseError> {

        let ipv4 = try!(get_ip_from(remote));
        if is_server {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 6900),
                remote_connection: None,
            })
        } else {
            Ok(EndPoint {
                local_connection: SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 9990),
                remote_connection: Some(SocketAddrV4::new(ipv4, 6900)),
            })
        }
    }

    pub fn start_listen(&mut self) -> Result<(), Error> {
        // Bind the socket
        let socket = try!(UdpSocket::bind(self.local_connection));

        // Start servicing requests from a client
        // Read from the socket
        let mut buf = [0u8; 516]; //UDP packet is 516 bytes
        let (amt, src) = try!(socket.recv_from(&mut buf));
        let mut last_blk_id = 0u16;
        match buf[1] {
            1 => {
                // RRQ
                println!("RRQ received");
                // Get filename to be sent from the packet
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }
                let filename = str::from_utf8(&buf[2..i]);
                println!("file requested: {}", filename.unwrap());
                // Send data via the socket
                let mut fs = FileStreamReader::new(String::from(filename.unwrap())).unwrap();
                let (mut buf, num_bytes_read) = fs.next().unwrap();
                let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                try!(socket.send_to(v.as_slice(), src));
                // TODO start timer here
                loop {
                    // Get ACK
                    let (amt, src) = try!(socket.recv_from(&mut buf));
                    if buf[1] == 4 {
                        println!("ACK received");
                        last_blk_id += 1;
                        let (buf, num_bytes_read) = fs.next().unwrap();
                        let v = self.create_data_packet(last_blk_id, &buf, num_bytes_read);
                        try!(socket.send_to(v.as_slice(), src));
                    }
                }
            },
            2 => {
                //WRQ
                println!("WRQ received");
                let mut i = 2;
                while buf[i] != '\0' as u8 {
                    i += 1;
                }

                //TODO fix the vec use to copy slice from buf
                let mut v = vec![];
                v.copy_from_slice(&buf[2..i]);
                let filename = str::from_utf8(v.as_slice());
                println!("file requested: {}", filename.unwrap());
                let mut writer = FileStreamWriter::new(String::from(filename.unwrap())).unwrap();
                //send ACK
                let mut block_num = 0u16;
                loop{
                    let low = block_num & 0x00FF;
                    let high = (block_num & 0xFF00) >> 8;

                    println!("Sending ACK");
                    socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    Self::clear_buf(&mut buf);
                    let (amt, src) = try!(socket.recv_from(&mut buf));

                    writer.append(&mut buf);
                    if amt - 4 < 512{
                        //TODO do we close writer here?
                        break;
                    }
                    block_num += 1;

                }

            },
            4 => {}
            _ => panic!("unrecognized packet type"),
        }

        Ok(())

    }

    fn create_error_packet(&mut self, error_code: ErrorCode, err_msg : &str)->Vec<u8>{
        let mut v = Vec::with_capacity(2+2+err_msg.len()+1);
        v.push(0);
        v.push(PacketType::ERROR as u8);
        v.push(0);
        v.push(error_code as u8);
        v.extend_from_slice(err_msg.as_bytes());
        v.push('\0' as u8);
        v
    }

    fn clear_buf(buf : &mut[u8]){
        assert!(buf.len() == 516);
        for i in 0..516{
            buf[i] = 0;
        }
    }

    fn create_data_packet(&mut self, blk_id: u16, buf: &[u8], num_bytes: usize) -> Vec<u8> {
        let mut v = Vec::with_capacity(num_bytes + 4);
        v.push(0);
        v.push(PacketType::DATA as u8);
        v.push((blk_id >> 8) as u8);
        v.push((blk_id & 0x00FF) as u8);
        v.extend_from_slice(&buf[0..num_bytes]);
        v
    }

    fn create_rrq_wrq_packet(&self,
                             p_type: PacketType,
                             file_name: &str,
                             mode: &'static str)
                             -> Vec<u8> {
        let mut v = Vec::with_capacity(2 + file_name.len() + 1 + mode.len() + 1);
        v.push(0);
        v.push(p_type as u8);
        v.extend(file_name.as_bytes());
        v.push('\0' as u8);
        v.extend(mode.as_bytes());
        v.push('\0');//zero terminator
        v
    }

    pub fn get(&mut self, files: &[&str], mode: &'static str) -> Result<(), Error> {
        let socket = try!(UdpSocket::bind(self.local_connection));
        let mut buf = [0; 516];
        for file in files {
            // start with a RRQ
            println!("sending RRQ request");
            let packet = self.create_rrq_wrq_packet(PacketType::RRQ, file, mode);
            try!(socket.send_to(packet.as_slice(), self.remote_connection.unwrap()));
            // TODO remove the hardcoded filename
            let path = Path::new(file);

            println!("Downloading as ./{}", path.file_name().unwrap().to_str().unwrap());
            //TODO fix downloading very file in the current dir
            let mut writer =
                FileStreamWriter::new(format!("./{}", path.file_name().unwrap().to_str().unwrap()))
                    .unwrap();

            loop {
                // get the first block of the requested file
                let (amt, src) = try!(socket.recv_from(&mut buf));
                println!("Received block");
                println!("Total bytes read {}", amt);

                match buf[1] {
                    3 => {
                        let block_size = amt - 4;
                        println!("File block size is {:?}", block_size);
                        if block_size == 0 {
                            println!("Empty block recvd");
                            writer.close();
                            break;
                        }
                        println!("Appending to file");
                        writer.append(&buf[4..4 + block_size]);
                        let block_num: u16 = 0u16 | (buf[2] as u16) << 8 | buf[3] as u16;

                        // send ACK
                        let low = block_num & 0x00FF;
                        let high = (block_num & 0xFF00) >> 8;

                        println!("Sending ACK");
                        socket.send_to(&[0, PacketType::ACK as u8, high as u8, low as u8], src);

                    }
                    _ => {}
                }
                // Flush buf for next read
                // TODO fix this:
                for i in 0..516 {
                    buf[0] = 0;
                }
            }

        }

        Ok(())
    }
}

fn get_ip_from(ip: &str) -> Result<Ipv4Addr, AddrParseError> {
    Ipv4Addr::from_str(ip)
}
<|endoftext|>


<commit_before>use hashbrown::HashMap;

use super::utils::get_trigrams_with_positions;
use super::{LangProfile, LangProfileList};
use super::{Trigram, MAX_TOTAL_DISTANCE, MAX_TRIGRAM_DISTANCE};
use super::{ARABIC_LANGS, CYRILLIC_LANGS, DEVANAGARI_LANGS, HEBREW_LANGS, LATIN_LANGS};
use crate::core::{calculate_confidence, AllowList, Info, InternalQuery, Text};
use crate::scripts::grouping::MultiLangScript;
use crate::Lang;

#[derive(Debug)]
pub struct RawOutcome {
    pub trigrams_count: usize,
    pub raw_distances: Vec<(Lang, u32)>,
    pub scores: Vec<(Lang, f64)>,
}

pub fn detect(iquery: &mut InternalQuery) -> Option<Info> {
    let raw_outcome = raw_detect(iquery);
    let RawOutcome {
        trigrams_count,
        scores,
        ..
    } = raw_outcome;

    let mut raw_scores_iter = scores.into_iter();

    let opt_lang_score1 = raw_scores_iter.next();
    let opt_lang_score2 = raw_scores_iter.next();

    // TODO: Logic is duplicated in alphabets. Consider refactoring
    opt_lang_score1.map(|(lang1, score1)| {
        let script = iquery.multi_lang_script.to_script();
        let confidence = if let Some((_, score2)) = opt_lang_score2 {
            calculate_confidence(score1, score2, trigrams_count)
        } else {
            1.0
        };
        Info::new(script, lang1, confidence)
    })
}

pub fn raw_detect(iquery: &mut InternalQuery) -> RawOutcome {
    let lang_profile_list = script_to_lang_profile_list(iquery.multi_lang_script);
    calculate_scores_in_profiles(&mut iquery.text, &iquery.allow_list, lang_profile_list)
}

fn script_to_lang_profile_list(script: MultiLangScript) -> LangProfileList {
    use MultiLangScript as MLS;
    match script {
        MLS::Latin => LATIN_LANGS,
        MLS::Cyrillic => CYRILLIC_LANGS,
        MLS::Arabic => ARABIC_LANGS,
        MLS::Devanagari => DEVANAGARI_LANGS,
        MLS::Hebrew => HEBREW_LANGS,
    }
}

fn calculate_scores_in_profiles(
    text: &mut Text,
    allow_list: &AllowList,
    lang_profile_list: LangProfileList,
) -> RawOutcome {
    let mut lang_distances: Vec<(Lang, u32)> = vec![];

    let trigrams = get_trigrams_with_positions(&text.lowercase());
    let trigrams_count = trigrams.len();

    for &(lang, lang_trigrams) in lang_profile_list {
        if !allow_list.is_allowed(lang) {
            continue;
        }
        let dist = calculate_distance(lang_trigrams, &trigrams);
        lang_distances.push(((lang), dist));
    }

    // Sort languages by distance
    lang_distances.sort_by_key(|key| key.1);

    let raw_scores = lang_distances
        .iter()
        .map(|&(lang, distance)| (lang, distance_to_raw_score(distance)))
        .collect();

    // TODO: CALCULATE NORMALIZED SCORES

    RawOutcome {
        trigrams_count,
        scores: raw_scores,
        raw_distances: lang_distances,
    }
}

fn calculate_distance(lang_trigrams: LangProfile, text_trigrams: &HashMap<Trigram, u32>) -> u32 {
    let mut total_dist = 0u32;

    for (i, &trigram) in lang_trigrams.iter().enumerate() {
        let dist = match text_trigrams.get(&trigram) {
            Some(&n) => (n as i32 - i as i32).abs() as u32,
            None => MAX_TRIGRAM_DISTANCE,
        };
        total_dist += dist;
    }
    if total_dist < MAX_TOTAL_DISTANCE {
        total_dist
    } else {
        MAX_TOTAL_DISTANCE
    }
}

fn distance_to_raw_score(distance: u32) -> f64 {
    let similarity = MAX_TOTAL_DISTANCE - distance;
    similarity as f64 / MAX_TOTAL_DISTANCE as f64
}

// fn min_perfect_distance(count_trigrams: usize) -> usize {
//     // 1 = 0 + 299*300
//     // 2 = 0 + 1 + 298*300
//     // 3 = 0 + 0 + 298*300
//     // 4 = 3 + 2 + 1
// }

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

    #[test]
    fn test_when_german_is_given() {
        let text = "Die Ordnung muss für immer in diesem Codebase bleiben";
        let mut iq = InternalQuery {
            text: Text::new(text),
            allow_list: &AllowList::all(),
            multi_lang_script: MultiLangScript::Latin,
        };
        let raw_outcome = raw_detect(&mut iq);

        assert_eq!(raw_outcome.trigrams_count, 50);

        let &(first_lang, first_score) = raw_outcome.scores.first().unwrap();
        let &(_last_lang, last_score) = raw_outcome.scores.last().unwrap();

        assert_eq!(first_lang, Lang::Deu);

        assert!(first_score >= 0.0);
        assert!(first_score <= 1.0);

        assert!(last_score >= 0.0);
        assert!(last_score <= 1.0);

        //println!("{:#?}", raw_outcome);
    }
}
<commit_msg>Add explaning comment<commit_after>use hashbrown::HashMap;

use super::utils::get_trigrams_with_positions;
use super::{LangProfile, LangProfileList};
use super::{Trigram, MAX_TOTAL_DISTANCE, MAX_TRIGRAM_DISTANCE};
use super::{ARABIC_LANGS, CYRILLIC_LANGS, DEVANAGARI_LANGS, HEBREW_LANGS, LATIN_LANGS};
use crate::core::{calculate_confidence, AllowList, Info, InternalQuery, Text};
use crate::scripts::grouping::MultiLangScript;
use crate::Lang;

#[derive(Debug)]
pub struct RawOutcome {
    pub trigrams_count: usize,
    pub raw_distances: Vec<(Lang, u32)>,
    pub scores: Vec<(Lang, f64)>,
}

pub fn detect(iquery: &mut InternalQuery) -> Option<Info> {
    let raw_outcome = raw_detect(iquery);
    let RawOutcome {
        trigrams_count,
        scores,
        ..
    } = raw_outcome;

    let mut raw_scores_iter = scores.into_iter();

    let opt_lang_score1 = raw_scores_iter.next();
    let opt_lang_score2 = raw_scores_iter.next();

    // TODO: Logic is duplicated in alphabets. Consider refactoring
    opt_lang_score1.map(|(lang1, score1)| {
        let script = iquery.multi_lang_script.to_script();
        let confidence = if let Some((_, score2)) = opt_lang_score2 {
            calculate_confidence(score1, score2, trigrams_count)
        } else {
            1.0
        };
        Info::new(script, lang1, confidence)
    })
}

pub fn raw_detect(iquery: &mut InternalQuery) -> RawOutcome {
    let lang_profile_list = script_to_lang_profile_list(iquery.multi_lang_script);
    calculate_scores_in_profiles(&mut iquery.text, &iquery.allow_list, lang_profile_list)
}

fn script_to_lang_profile_list(script: MultiLangScript) -> LangProfileList {
    use MultiLangScript as MLS;
    match script {
        MLS::Latin => LATIN_LANGS,
        MLS::Cyrillic => CYRILLIC_LANGS,
        MLS::Arabic => ARABIC_LANGS,
        MLS::Devanagari => DEVANAGARI_LANGS,
        MLS::Hebrew => HEBREW_LANGS,
    }
}

fn calculate_scores_in_profiles(
    text: &mut Text,
    allow_list: &AllowList,
    lang_profile_list: LangProfileList,
) -> RawOutcome {
    let mut lang_distances: Vec<(Lang, u32)> = vec![];

    let trigrams = get_trigrams_with_positions(&text.lowercase());
    let trigrams_count = trigrams.len();

    for &(lang, lang_trigrams) in lang_profile_list {
        if !allow_list.is_allowed(lang) {
            continue;
        }
        let dist = calculate_distance(lang_trigrams, &trigrams);
        lang_distances.push(((lang), dist));
    }

    // Sort languages by distance
    lang_distances.sort_by_key(|key| key.1);

    let raw_scores = lang_distances
        .iter()
        .map(|&(lang, distance)| (lang, distance_to_raw_score(distance)))
        .collect();

    // TODO: CALCULATE NORMALIZED SCORES

    RawOutcome {
        trigrams_count,
        scores: raw_scores,
        raw_distances: lang_distances,
    }
}

fn calculate_distance(lang_trigrams: LangProfile, text_trigrams: &HashMap<Trigram, u32>) -> u32 {
    let mut total_dist = 0u32;

    for (i, &trigram) in lang_trigrams.iter().enumerate() {
        let dist = match text_trigrams.get(&trigram) {
            Some(&n) => (n as i32 - i as i32).abs() as u32,
            None => MAX_TRIGRAM_DISTANCE,
        };
        total_dist += dist;
    }
    if total_dist < MAX_TOTAL_DISTANCE {
        total_dist
    } else {
        MAX_TOTAL_DISTANCE
    }
}

// NOTE:
// MAX_TOTAL_DISTANCE = 90_000
// Even in case of very large text, the wining language has distance about 45_000 (half).
// This results into best score of about 0.5.
// On short texts the best score is extremely small. Consider normalization strategies
// (see min_perfect_distance() below)
fn distance_to_raw_score(distance: u32) -> f64 {
    let similarity = MAX_TOTAL_DISTANCE - distance;
    similarity as f64 / MAX_TOTAL_DISTANCE as f64
}

// fn min_perfect_distance(count_trigrams: usize) -> usize {
//     // 1 = 0 + 299*300
//     // 2 = 0 + 1 + 298*300
//     // 3 = 0 + 0 + 298*300
//     // 4 = 3 + 2 + 1
// }

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

    #[test]
    fn test_when_german_is_given() {
        let text = "Die Ordnung muss für immer in diesem Codebase bleiben";
        let mut iq = InternalQuery {
            text: Text::new(text),
            allow_list: &AllowList::all(),
            multi_lang_script: MultiLangScript::Latin,
        };
        let raw_outcome = raw_detect(&mut iq);

        assert_eq!(raw_outcome.trigrams_count, 50);

        let &(first_lang, first_score) = raw_outcome.scores.first().unwrap();
        let &(_last_lang, last_score) = raw_outcome.scores.last().unwrap();

        assert_eq!(first_lang, Lang::Deu);

        assert!(first_score >= 0.0);
        assert!(first_score <= 1.0);

        assert!(last_score >= 0.0);
        assert!(last_score <= 1.0);

        //println!("{:#?}", raw_outcome);
    }
}
<|endoftext|>


<commit_before>//#![recursion_limit = "256"]
// http://stackoverflow.com/questions/25877285/how-to-disable-unused-code-warnings-in-rust
// https://users.rust-lang.org/t/turning-off-compiler-warning-messages/4975/2

mod api;
mod files;

use serde_derive::Serialize;

use dotenv;
use std::env;

use actix_web::{guard, middleware, web, App, Error as ActixError, HttpResponse, HttpServer};

use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
use tokio_postgres::NoTls;

#[macro_use]
extern crate horrorshow;
use horrorshow::helper::doctype;
use horrorshow::{RenderBox, Template};

#[derive(Serialize)]
struct Context {
    name: String,
}

#[derive(Serialize, Debug)]
struct TemplateContext {
    parent: String,
    name: String,
    content: String,
    items: Vec<String>,
}

async fn template(ssr: web::Path<bool>) -> Result<HttpResponse, ActixError> {
    let s: String = if *ssr {
        "true".to_owned()
    } else {
        "false".to_owned()
    };

    let posts = vec![
        Post {
            title: String::from("First Post"),
            tags: vec![String::from("first post")],
            body: String::from("My Test Post"),
        },
        Post {
            title: String::from("Second Post"),
            tags: vec![],
            body: String::from("My Second Test Post"),
        },
    ];

    let doc_str = render("my blog", posts.into_iter(), &s.to_owned());

    Ok(HttpResponse::Ok().content_type("text/html").body(doc_str))
}

fn render_post(post: Post) -> Box<dyn RenderBox> {
    let Post { title, body, tags } = post;
    box_html! {
        article {
            header(class="post-header") {
                h1 : title;
                ul {
                    @ for tag in tags {
                        li : tag
                    }
                }
            }
            section(class="post-body") : body;
        }
    }
}

fn render<I: Iterator<Item = Post>>(title: &str, posts: I, elm_string: &str) -> String {
    (html! {
        : doctype::HTML;
        html {
            head {
                title : title
            }
            body {
                main {
                    header { h1 : title }
                    section(id="posts") {
                        @ for post in posts {
                            : render_post(post)
                        }
                    }
                    div: elm_string
                }
            }
        }
    })
    .into_string()
    .unwrap()
}

struct Post {
    title: String,
    tags: Vec<String>,
    body: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Error: NotPresent
    // Chaning to specific file, bc `strace ./target/debug/rustommerce` shown,
    // that dotenv searches up for .env till it reaches /
    // https://galenguyer.com/blog/2020/05/19/docker-rust-notpresent
    let my_path = env::current_dir().map(|a| a.join("../.env"))?;
    dotenv::from_path(my_path.as_path())?;

    env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();
    let endpoint = format!("127.0.0.1:{}", env::var("SERVER_PORT")?);

    let connection_string = format!(
        "postgres://{}:{}@{}:{}/{}",
        &env::var("DBUSER")?,
        &env::var("DBPASS")?,
        &env::var("DBHOST")?,
        &env::var("DBPORT")?,
        &env::var("DBNAME")?,
    );
    // Idea for parse from
    // https://github.com/OneSignal/L3-37/blob/2031639d1f3a7aa00a5a741b87b464e19d995d21/l337-postgres/src/lib.rs#L217
    let pg_config: tokio_postgres::Config = connection_string.parse()?;
    let mgr_config = ManagerConfig {
        recycling_method: RecyclingMethod::Fast,
    };
    let mgr = Manager::from_config(pg_config, NoTls, mgr_config);
    let pool = Pool::new(mgr, 16);

    let sys = actix_rt::System::new("app");
    HttpServer::new(move || {
        App::new()
            .data(pool.clone())
            .wrap(middleware::Logger::default())
            .service(web::resource("/template/{ssr}").route(web::get().to(template)))
            .service(web::resource("/products").route(web::get().to(api::handlers::get_products)))
            .service(web::resource("/userInformation").route(web::get().to(api::user_information)))
            .service(web::resource("/dataSets").route(web::get().to(api::data_sets)))
            .service(web::resource("/dataSets/{url}").route(web::get().to(api::data_set)))
            .service(
                web::resource("/dataSetsCategories")
                    .route(web::get().to(api::data_sets_categories)),
            )
            .service(
                web::resource("/dataSetsCategories/{url}")
                    .route(web::get().to(api::data_set_category)),
            )
            .service(web::resource("/favicon").route(web::get().to(files::favicon)))
            .service(web::resource("/styles/{file:.*}").route(web::get().to(files::styles)))
            .service(web::resource("/js/{file:.*}").route(web::get().to(files::js)))
            .default_service(
                // 404 for GET request
                web::resource("")
                    .route(web::get().to(files::index))
                    // all requests that are not `GET`
                    .route(
                        web::route()
                            .guard(guard::Not(guard::Get()))
                            .to(HttpResponse::MethodNotAllowed),
                    ),
            )
    })
    .bind(&endpoint)?
    .run();
    // If you do not return `Ok(())` but just `.run()`, this will happend:
    // When main returns `Result<(), Box<dyn std::error::Error + 'static + Send + Sync>> instead of
    // `io::Result<()>`, there is error and `into()` is needed.
    // expected struct `std::boxed::Box`, found struct `std::io::Error`
    // https://users.rust-lang.org/t/boxing-errors-in-result-throws-type-mismatch/36692/2
    //.map_err(|e| e.into())

    println!("Server running at {:?}", endpoint);
    let _ = sys.run();

    Ok(())
}
<commit_msg>server: add api/ scope<commit_after>//#![recursion_limit = "256"]
// http://stackoverflow.com/questions/25877285/how-to-disable-unused-code-warnings-in-rust
// https://users.rust-lang.org/t/turning-off-compiler-warning-messages/4975/2

mod api;
mod files;

use serde_derive::Serialize;

use dotenv;
use std::env;

use actix_web::{guard, middleware, web, App, Error as ActixError, HttpResponse, HttpServer};

use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
use tokio_postgres::NoTls;

#[macro_use]
extern crate horrorshow;
use horrorshow::helper::doctype;
use horrorshow::{RenderBox, Template};

#[derive(Serialize)]
struct Context {
    name: String,
}

#[derive(Serialize, Debug)]
struct TemplateContext {
    parent: String,
    name: String,
    content: String,
    items: Vec<String>,
}

async fn template(ssr: web::Path<bool>) -> Result<HttpResponse, ActixError> {
    let s: String = if *ssr {
        "true".to_owned()
    } else {
        "false".to_owned()
    };

    let posts = vec![
        Post {
            title: String::from("First Post"),
            tags: vec![String::from("first post")],
            body: String::from("My Test Post"),
        },
        Post {
            title: String::from("Second Post"),
            tags: vec![],
            body: String::from("My Second Test Post"),
        },
    ];

    let doc_str = render("my blog", posts.into_iter(), &s.to_owned());

    Ok(HttpResponse::Ok().content_type("text/html").body(doc_str))
}

fn render_post(post: Post) -> Box<dyn RenderBox> {
    let Post { title, body, tags } = post;
    box_html! {
        article {
            header(class="post-header") {
                h1 : title;
                ul {
                    @ for tag in tags {
                        li : tag
                    }
                }
            }
            section(class="post-body") : body;
        }
    }
}

fn render<I: Iterator<Item = Post>>(title: &str, posts: I, elm_string: &str) -> String {
    (html! {
        : doctype::HTML;
        html {
            head {
                title : title
            }
            body {
                main {
                    header { h1 : title }
                    section(id="posts") {
                        @ for post in posts {
                            : render_post(post)
                        }
                    }
                    div: elm_string
                }
            }
        }
    })
    .into_string()
    .unwrap()
}

struct Post {
    title: String,
    tags: Vec<String>,
    body: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Error: NotPresent
    // Chaning to specific file, bc `strace ./target/debug/rustommerce` shown,
    // that dotenv searches up for .env till it reaches /
    // https://galenguyer.com/blog/2020/05/19/docker-rust-notpresent
    let my_path = env::current_dir().map(|a| a.join("../.env"))?;
    dotenv::from_path(my_path.as_path())?;

    env::set_var("RUST_LOG", "actix_web=info");
    env_logger::init();
    let endpoint = format!("127.0.0.1:{}", env::var("SERVER_PORT")?);

    let connection_string = format!(
        "postgres://{}:{}@{}:{}/{}",
        &env::var("DBUSER")?,
        &env::var("DBPASS")?,
        &env::var("DBHOST")?,
        &env::var("DBPORT")?,
        &env::var("DBNAME")?,
    );
    // Idea for parse from
    // https://github.com/OneSignal/L3-37/blob/2031639d1f3a7aa00a5a741b87b464e19d995d21/l337-postgres/src/lib.rs#L217
    let pg_config: tokio_postgres::Config = connection_string.parse()?;
    let mgr_config = ManagerConfig {
        recycling_method: RecyclingMethod::Fast,
    };
    let mgr = Manager::from_config(pg_config, NoTls, mgr_config);
    let pool = Pool::new(mgr, 16);

    let sys = actix_rt::System::new("app");
    HttpServer::new(move || {
        App::new()
            .data(pool.clone())
            .wrap(middleware::Logger::default())
            .service(
                web::scope("/api/")
                    .service(
                        web::resource("/products")
                            .route(web::get().to(api::handlers::get_products)),
                    )
                    .service(
                        web::resource("/userInformation")
                            .route(web::get().to(api::user_information)),
                    )
                    .service(web::resource("/dataSets").route(web::get().to(api::data_sets)))
                    .service(web::resource("/dataSets/{url}").route(web::get().to(api::data_set)))
                    .service(
                        web::resource("/dataSetsCategories")
                            .route(web::get().to(api::data_sets_categories)),
                    )
                    .service(
                        web::resource("/dataSetsCategories/{url}")
                            .route(web::get().to(api::data_set_category)),
                    ),
            )
            .service(web::resource("/template/{ssr}").route(web::get().to(template)))
            .service(web::resource("/favicon").route(web::get().to(files::favicon)))
            .service(web::resource("/styles/{file:.*}").route(web::get().to(files::styles)))
            .service(web::resource("/js/{file:.*}").route(web::get().to(files::js)))
            .default_service(
                // 404 for GET request
                web::resource("")
                    .route(web::get().to(files::index))
                    // all requests that are not `GET`
                    .route(
                        web::route()
                            .guard(guard::Not(guard::Get()))
                            .to(HttpResponse::MethodNotAllowed),
                    ),
            )
    })
    .bind(&endpoint)?
    .run();
    // If you do not return `Ok(())` but just `.run()`, this will happend:
    // When main returns `Result<(), Box<dyn std::error::Error + 'static + Send + Sync>> instead of
    // `io::Result<()>`, there is error and `into()` is needed.
    // expected struct `std::boxed::Box`, found struct `std::io::Error`
    // https://users.rust-lang.org/t/boxing-errors-in-result-throws-type-mismatch/36692/2
    //.map_err(|e| e.into())

    println!("Server running at {:?}", endpoint);
    let _ = sys.run();

    Ok(())
}
<|endoftext|>