<commit_before>use godcoin::{net::*, prelude::{KeyPair, PrivateKey, Wif}};
use rustyline::{error::ReadlineError, Editor};
use std::{path::PathBuf, io::{Read, Cursor}};
use reqwest::{Url, Client};

mod db;
mod parser;

use self::db::{Db, DbState, Password};

macro_rules! check_unlocked {
    ($self:expr) => {
        if $self.db.state() != DbState::Unlocked {
            return Err("wallet not unlocked".to_owned());
        }
    };
}

macro_rules! check_args {
    ($args:expr, $count:expr) => {
        if $args.len() != $count {
            return Err("Missing arguments or too many provided".to_owned());
        }
    };
}

pub struct Wallet {
    prompt: String,
    url: Url,
    db: Db,
}

impl Wallet {
    pub fn new(home: PathBuf) -> Wallet {
        let db = Db::new(home.join("db"));
        let prompt = (if db.state() == DbState::Locked {
            "locked>> "
        } else {
            "new>> "
        })
        .to_owned();
        Wallet { db, prompt, url: "http://localhost:7777".parse().unwrap() }
    }

    pub fn start(mut self) {
        let mut rl = Editor::<()>::new();
        loop {
            let readline = rl.readline(&self.prompt);
            match readline {
                Ok(line) => {
                    if line.is_empty() {
                        continue;
                    }
                    let mut args = parser::parse_line(&line);

                    match self.process_line(&mut args) {
                        Ok(store_history) => {
                            if store_history {
                                rl.add_history_entry(line);
                            } else {
                                sodiumoxide::utils::memzero(&mut line.into_bytes());
                            }
                        }
                        Err(s) => {
                            println!("{}", s);
                            sodiumoxide::utils::memzero(&mut line.into_bytes());
                        }
                    }

                    for a in args {
                        sodiumoxide::utils::memzero(&mut a.into_bytes());
                    }
                }
                Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
                    println!("Closing walllet...");
                    break;
                }
                Err(err) => {
                    println!("Error reading input: {:?}", err);
                    break;
                }
            }
        }
    }

    fn process_line(&mut self, args: &mut Vec<String>) -> Result<bool, String> {
        if args.len() == 0 {
            return Ok(false);
        }
        match &*args[0] {
            "new" => {
                let state = self.db.state();
                if state != DbState::New {
                    if state == DbState::Locked {
                        println!("Use unlock to use the existing wallet");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Existing wallet already unlocked");
                        return Ok(false);
                    } else {
                        return Err(format!("Unknown state: {:?}", state));
                    }
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                self.db.set_password(pass);
                self.prompt = "locked>> ".to_owned();
                return Ok(false);
            }
            "unlock" => {
                let state = self.db.state();
                if state != DbState::Locked {
                    if state == DbState::New {
                        println!("A wallet has not yet been created, use new to create one");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Wallet already unlocked");
                        return Ok(false);
                    }
                    return Err(format!("Unknown state: {:?}", state));
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                if self.db.unlock(pass) {
                    self.prompt = "unlocked>> ".to_owned();
                } else {
                    println!("Failed to unlock wallet...incorrect password");
                }
                return Ok(false);
            }
            "create_account" => {
                check_unlocked!(self);
                let account = &args[1];
                if self.db.get_account(account).is_some() {
                    println!("Account already exists");
                    return Ok(true);
                }
                let key = KeyPair::gen_keypair();
                self.db.set_account(account, &key.1);
                println!("Public key => {}", key.0.to_wif());
                println!("Private key => {}", key.1.to_wif());
            }
            "import_account" => {
                check_unlocked!(self);
                check_args!(args, 3);
                let account = &args[1];
                let wif = PrivateKey::from_wif(&args[2]).map_err(|_| "Invalid wif".to_owned())?;
                for (acc, pair) in self.db.get_accounts() {
                    if &acc == account {
                        println!("Account already exists");
                        return Ok(true);
                    } else if pair.1 == wif.1 {
                        println!("Wif already exists under account `{}`", &acc);
                        return Ok(true);
                    }
                }
                self.db.set_account(account, &wif.1);
            }
            "get_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                let key = self.db.get_account(&args[1]);
                match key {
                    Some(key) => {
                        println!("Public key => {}", key.0.to_wif());
                        println!("Private key => {}", key.1.to_wif());
                    }
                    None => {
                        println!("Account not found");
                    }
                }
            }
            "delete_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                if self.db.del_account(&args[1]) {
                    println!("Account permanently deleted");
                } else {
                    println!("Account not found");
                }
            }
            "list_accounts" => {
                check_unlocked!(self);
                println!("Accounts:");
                for (acc, key) in self.db.get_accounts() {
                    println!("  {} => {}", acc, key.0.to_wif());
                }
            }
            "get_block" => {
                check_args!(args, 2);
                let height: u64 = args[1].parse().map_err(|_| {
                    "Failed to parse height argument".to_owned()
                })?;
                let client = Client::new();
                let res = client.post(self.url.clone())
                    .body(MsgRequest::GetBlock(height).serialize())
                    .send();
                match res {
                    Ok(mut res) => {
                        let len = res.content_length().unwrap_or(0);
                        let mut content = Vec::with_capacity(len as usize);
                        res.read_to_end(&mut content).map_err(|e| {
                            format!("{}", e)
                        })?;
                        let mut cursor = Cursor::<&[u8]>::new(&content);
                        let res = MsgResponse::deserialize(&mut cursor).map_err(|e| {
                            format!("Failed to deserialize response: {}", e)
                        })?;
                        println!("{:#?}", res);
                    },
                    Err(e) => {
                        return Err(format!("{}", e))
                    }
                }
            }
            "help" => {
                Self::print_usage("Displaying help...");
            }
            _ => {
                Self::print_usage(&format!("Invalid command: {}", args[0]));
            }
        }
        Ok(true)
    }

    fn print_usage(header: &str) {
        let mut cmds = Vec::<[&str; 2]>::new();
        cmds.push(["help", "Display this help menu"]);
        cmds.push(["new <password>", "Create a new wallet"]);
        cmds.push(["unlock <password>", "Unlock an existing wallet"]);
        cmds.push(["create_account <account>", "Create an account"]);
        cmds.push(["import_account <account> <wif>", "Import an account"]);
        cmds.push(["delete_account <account>", "Delete an existing account"]);
        cmds.push(["get_account <account>", "Retrieve account information"]);
        cmds.push(["list_accounts", "List all accounts"]);
        cmds.push(["list_accounts", "List all accounts"]);

        let mut max_len = 0;
        for cmd in &cmds {
            assert!(cmd.len() == 2);
            let cmd_len = cmd[0].len();
            if cmd_len > max_len {
                max_len = cmd_len;
            }
        }

        println!("{}\n", header);
        for cmd in &cmds {
            let mut c = cmd[0].to_owned();
            if c.len() < max_len {
                for _ in 0..max_len - c.len() {
                    c.push(' ');
                }
            }
            println!("  {}  {}", c, cmd[1]);
        }
        println!("");
    }
}
<commit_msg>cargo fmt sources<commit_after>use godcoin::{
    net::*,
    prelude::{KeyPair, PrivateKey, Wif},
};
use reqwest::{Client, Url};
use rustyline::{error::ReadlineError, Editor};
use std::{
    io::{Cursor, Read},
    path::PathBuf,
};

mod db;
mod parser;

use self::db::{Db, DbState, Password};

macro_rules! check_unlocked {
    ($self:expr) => {
        if $self.db.state() != DbState::Unlocked {
            return Err("wallet not unlocked".to_owned());
        }
    };
}

macro_rules! check_args {
    ($args:expr, $count:expr) => {
        if $args.len() != $count {
            return Err("Missing arguments or too many provided".to_owned());
        }
    };
}

pub struct Wallet {
    prompt: String,
    url: Url,
    db: Db,
}

impl Wallet {
    pub fn new(home: PathBuf) -> Wallet {
        let db = Db::new(home.join("db"));
        let prompt = (if db.state() == DbState::Locked {
            "locked>> "
        } else {
            "new>> "
        })
        .to_owned();
        Wallet {
            db,
            prompt,
            url: "http://localhost:7777".parse().unwrap(),
        }
    }

    pub fn start(mut self) {
        let mut rl = Editor::<()>::new();
        loop {
            let readline = rl.readline(&self.prompt);
            match readline {
                Ok(line) => {
                    if line.is_empty() {
                        continue;
                    }
                    let mut args = parser::parse_line(&line);

                    match self.process_line(&mut args) {
                        Ok(store_history) => {
                            if store_history {
                                rl.add_history_entry(line);
                            } else {
                                sodiumoxide::utils::memzero(&mut line.into_bytes());
                            }
                        }
                        Err(s) => {
                            println!("{}", s);
                            sodiumoxide::utils::memzero(&mut line.into_bytes());
                        }
                    }

                    for a in args {
                        sodiumoxide::utils::memzero(&mut a.into_bytes());
                    }
                }
                Err(ReadlineError::Interrupted) | Err(ReadlineError::Eof) => {
                    println!("Closing walllet...");
                    break;
                }
                Err(err) => {
                    println!("Error reading input: {:?}", err);
                    break;
                }
            }
        }
    }

    fn process_line(&mut self, args: &mut Vec<String>) -> Result<bool, String> {
        if args.len() == 0 {
            return Ok(false);
        }
        match &*args[0] {
            "new" => {
                let state = self.db.state();
                if state != DbState::New {
                    if state == DbState::Locked {
                        println!("Use unlock to use the existing wallet");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Existing wallet already unlocked");
                        return Ok(false);
                    } else {
                        return Err(format!("Unknown state: {:?}", state));
                    }
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                self.db.set_password(pass);
                self.prompt = "locked>> ".to_owned();
                return Ok(false);
            }
            "unlock" => {
                let state = self.db.state();
                if state != DbState::Locked {
                    if state == DbState::New {
                        println!("A wallet has not yet been created, use new to create one");
                        return Ok(false);
                    } else if state == DbState::Unlocked {
                        println!("Wallet already unlocked");
                        return Ok(false);
                    }
                    return Err(format!("Unknown state: {:?}", state));
                }

                check_args!(args, 2);
                let pass = &Password(args.remove(1).into_bytes());
                if self.db.unlock(pass) {
                    self.prompt = "unlocked>> ".to_owned();
                } else {
                    println!("Failed to unlock wallet...incorrect password");
                }
                return Ok(false);
            }
            "create_account" => {
                check_unlocked!(self);
                let account = &args[1];
                if self.db.get_account(account).is_some() {
                    println!("Account already exists");
                    return Ok(true);
                }
                let key = KeyPair::gen_keypair();
                self.db.set_account(account, &key.1);
                println!("Public key => {}", key.0.to_wif());
                println!("Private key => {}", key.1.to_wif());
            }
            "import_account" => {
                check_unlocked!(self);
                check_args!(args, 3);
                let account = &args[1];
                let wif = PrivateKey::from_wif(&args[2]).map_err(|_| "Invalid wif".to_owned())?;
                for (acc, pair) in self.db.get_accounts() {
                    if &acc == account {
                        println!("Account already exists");
                        return Ok(true);
                    } else if pair.1 == wif.1 {
                        println!("Wif already exists under account `{}`", &acc);
                        return Ok(true);
                    }
                }
                self.db.set_account(account, &wif.1);
            }
            "get_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                let key = self.db.get_account(&args[1]);
                match key {
                    Some(key) => {
                        println!("Public key => {}", key.0.to_wif());
                        println!("Private key => {}", key.1.to_wif());
                    }
                    None => {
                        println!("Account not found");
                    }
                }
            }
            "delete_account" => {
                check_unlocked!(self);
                check_args!(args, 2);
                if self.db.del_account(&args[1]) {
                    println!("Account permanently deleted");
                } else {
                    println!("Account not found");
                }
            }
            "list_accounts" => {
                check_unlocked!(self);
                println!("Accounts:");
                for (acc, key) in self.db.get_accounts() {
                    println!("  {} => {}", acc, key.0.to_wif());
                }
            }
            "get_block" => {
                check_args!(args, 2);
                let height: u64 = args[1]
                    .parse()
                    .map_err(|_| "Failed to parse height argument".to_owned())?;
                let client = Client::new();
                let res = client
                    .post(self.url.clone())
                    .body(MsgRequest::GetBlock(height).serialize())
                    .send();
                match res {
                    Ok(mut res) => {
                        let len = res.content_length().unwrap_or(0);
                        let mut content = Vec::with_capacity(len as usize);
                        res.read_to_end(&mut content)
                            .map_err(|e| format!("{}", e))?;
                        let mut cursor = Cursor::<&[u8]>::new(&content);
                        let res = MsgResponse::deserialize(&mut cursor)
                            .map_err(|e| format!("Failed to deserialize response: {}", e))?;
                        println!("{:#?}", res);
                    }
                    Err(e) => return Err(format!("{}", e)),
                }
            }
            "help" => {
                Self::print_usage("Displaying help...");
            }
            _ => {
                Self::print_usage(&format!("Invalid command: {}", args[0]));
            }
        }
        Ok(true)
    }

    fn print_usage(header: &str) {
        let mut cmds = Vec::<[&str; 2]>::new();
        cmds.push(["help", "Display this help menu"]);
        cmds.push(["new <password>", "Create a new wallet"]);
        cmds.push(["unlock <password>", "Unlock an existing wallet"]);
        cmds.push(["create_account <account>", "Create an account"]);
        cmds.push(["import_account <account> <wif>", "Import an account"]);
        cmds.push(["delete_account <account>", "Delete an existing account"]);
        cmds.push(["get_account <account>", "Retrieve account information"]);
        cmds.push(["list_accounts", "List all accounts"]);
        cmds.push(["list_accounts", "List all accounts"]);

        let mut max_len = 0;
        for cmd in &cmds {
            assert!(cmd.len() == 2);
            let cmd_len = cmd[0].len();
            if cmd_len > max_len {
                max_len = cmd_len;
            }
        }

        println!("{}\n", header);
        for cmd in &cmds {
            let mut c = cmd[0].to_owned();
            if c.len() < max_len {
                for _ in 0..max_len - c.len() {
                    c.push(' ');
                }
            }
            println!("  {}  {}", c, cmd[1]);
        }
        println!("");
    }
}
<|endoftext|>


<commit_before>macro_rules! impl_uint_arith {
    ($(($name:ident, $n:ty)),+) => {
        $( impl<const LANES: usize> $name<LANES> where Self: crate::LanesAtMost64 {

            /// Lanewise saturating add.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::MAX;")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([2, 1, 0, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x + max;
            /// let sat = x.saturating_add(max);
            /// assert_eq!(x - 1, unsat);
            /// assert_eq!(sat, max);
            /// ```
            #[inline]
            pub fn saturating_add(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_add(self, second) }
            }

            /// Lanewise saturating subtract.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::MAX;")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([2, 1, 0, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x - max;
            /// let sat = x.saturating_sub(max);
            /// assert_eq!(unsat, x + 1);
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::splat(0));")]
            #[inline]
            pub fn saturating_sub(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_sub(self, second) }
            }
        })+
    }
}

macro_rules! impl_int_arith {
    ($(($name:ident, $n:ty)),+) => {
        $( impl<const LANES: usize> $name<LANES> where Self: crate::LanesAtMost64 {

            /// Lanewise saturating add.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([MIN, 0, 1, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x + max;
            /// let sat = x.saturating_add(max);
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([-1, MAX, MIN, -2]));")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([-1, MAX, MAX, MAX]));")]
            /// ```
            #[inline]
            pub fn saturating_add(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_add(self, second) }
            }

            /// Lanewise saturating subtract.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([MIN, -2, -1, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x - max;
            /// let sat = x.saturating_sub(max);
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([1, MAX, MIN, 0]));")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([MIN, MIN, MIN, 0]));")]
            #[inline]
            pub fn saturating_sub(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_sub(self, second) }
            }

            /// Lanewise saturating absolute value, implemented in Rust.
            ///
            /// # Examples
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::splat([MIN, -2, 0, 3]);")]
            /// let unsat = x.abs();
            /// let sat = x.saturating_abs();
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([MIN, 2, 0, 3]);")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([MAX, 2, 0, 3]));")]
            /// ```
            #[inline]
            pub fn saturating_abs(self) -> Self {
                // arith shift for -1 or 0 mask based on sign bit, giving 2s complement
                const SHR: $n = <$n>::BITS as $n - 1;
                let m = self >> SHR;
                (self^m).saturating_sub(m)
            }

            /// Lanewise saturating negation, implemented in Rust.
            ///
            /// # Examples
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::splat([MIN, -2, 3, MAX]);")]
            /// let unsat = -x;
            /// let sat = x.saturating_neg();
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([MIN, 2, -3, MIN + 1]);")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([MAX, 2, -3, MIN + 1]));")]
            /// ```
            #[inline]
            pub fn saturating_neg(self) -> Self {
                Self::splat(0).saturating_sub(self)
            }
        })+
    }
}

use crate::vector::*;

impl_uint_arith! { (SimdU8, u8), (SimdU16, u16), (SimdU32, u32), (SimdU64, u64), (SimdUsize, usize) }
impl_int_arith! { (SimdI8, i8), (SimdI16, i16), (SimdI32, i32), (SimdI64, i64), (SimdIsize, isize) }
<commit_msg>Explain why to use saturation<commit_after>macro_rules! impl_uint_arith {
    ($(($name:ident, $n:ty)),+) => {
        $( impl<const LANES: usize> $name<LANES> where Self: crate::LanesAtMost64 {

            /// Lanewise saturating add.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::MAX;")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([2, 1, 0, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x + max;
            /// let sat = x.saturating_add(max);
            /// assert_eq!(x - 1, unsat);
            /// assert_eq!(sat, max);
            /// ```
            #[inline]
            pub fn saturating_add(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_add(self, second) }
            }

            /// Lanewise saturating subtract.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::MAX;")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([2, 1, 0, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x - max;
            /// let sat = x.saturating_sub(max);
            /// assert_eq!(unsat, x + 1);
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::splat(0));")]
            #[inline]
            pub fn saturating_sub(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_sub(self, second) }
            }
        })+
    }
}

macro_rules! impl_int_arith {
    ($(($name:ident, $n:ty)),+) => {
        $( impl<const LANES: usize> $name<LANES> where Self: crate::LanesAtMost64 {

            /// Lanewise saturating add.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([MIN, 0, 1, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x + max;
            /// let sat = x.saturating_add(max);
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([-1, MAX, MIN, -2]));")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([-1, MAX, MAX, MAX]));")]
            /// ```
            #[inline]
            pub fn saturating_add(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_add(self, second) }
            }

            /// Lanewise saturating subtract.
            ///
            /// # Examples
            /// ```
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::from_array([MIN, -2, -1, MAX]);")]
            #[doc = concat!("let max = ", stringify!($name), "::splat(MAX);")]
            /// let unsat = x - max;
            /// let sat = x.saturating_sub(max);
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([1, MAX, MIN, 0]));")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([MIN, MIN, MIN, 0]));")]
            #[inline]
            pub fn saturating_sub(self, second: Self) -> Self {
                unsafe { crate::intrinsics::simd_saturating_sub(self, second) }
            }

            /// Lanewise saturating absolute value, implemented in Rust.
            /// As abs(), except the MIN value becomes MAX instead of itself.
            ///
            /// # Examples
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::splat([MIN, -2, 0, 3]);")]
            /// let unsat = x.abs();
            /// let sat = x.saturating_abs();
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([MIN, 2, 0, 3]);")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([MAX, 2, 0, 3]));")]
            /// ```
            #[inline]
            pub fn saturating_abs(self) -> Self {
                // arith shift for -1 or 0 mask based on sign bit, giving 2s complement
                const SHR: $n = <$n>::BITS as $n - 1;
                let m = self >> SHR;
                (self^m).saturating_sub(m)
            }

            /// Lanewise saturating negation, implemented in Rust.
            /// As neg(), except the MIN value becomes MAX instead of itself.
            ///
            /// # Examples
            /// # use core_simd::*;
            #[doc = concat!("# use core::", stringify!($n), "::{MIN, MAX};")]
            #[doc = concat!("let x = ", stringify!($name), "::splat([MIN, -2, 3, MAX]);")]
            /// let unsat = -x;
            /// let sat = x.saturating_neg();
            #[doc = concat!("assert_eq!(unsat, ", stringify!($name), "::from_array([MIN, 2, -3, MIN + 1]);")]
            #[doc = concat!("assert_eq!(sat, ", stringify!($name), "::from_array([MAX, 2, -3, MIN + 1]));")]
            /// ```
            #[inline]
            pub fn saturating_neg(self) -> Self {
                Self::splat(0).saturating_sub(self)
            }
        })+
    }
}

use crate::vector::*;

impl_uint_arith! { (SimdU8, u8), (SimdU16, u16), (SimdU32, u32), (SimdU64, u64), (SimdUsize, usize) }
impl_int_arith! { (SimdI8, i8), (SimdI16, i16), (SimdI32, i32), (SimdI64, i64), (SimdIsize, isize) }
<|endoftext|>


<commit_before>//! Searching for matches.

use crate::{
    matching,
    resolving::{ResolvedPath, ResolvedPattern, ResolvedRule},
    Match, MatchFinder,
};
use ide_db::{
    base_db::{FileId, FileRange},
    defs::Definition,
    search::{SearchScope, UsageSearchResult},
};
use rustc_hash::FxHashSet;
use syntax::{ast, AstNode, SyntaxKind, SyntaxNode};

/// A cache for the results of find_usages. This is for when we have multiple patterns that have the
/// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type
/// and as a pattern. In each, the usages of `foo::Bar` are the same and we'd like to avoid finding
/// them more than once.
#[derive(Default)]
pub(crate) struct UsageCache {
    usages: Vec<(Definition, UsageSearchResult)>,
}

impl<'db> MatchFinder<'db> {
    /// Adds all matches for `rule` to `matches_out`. Matches may overlap in ways that make
    /// replacement impossible, so further processing is required in order to properly nest matches
    /// and remove overlapping matches. This is done in the `nesting` module.
    pub(crate) fn find_matches_for_rule(
        &self,
        rule: &ResolvedRule,
        usage_cache: &mut UsageCache,
        matches_out: &mut Vec<Match>,
    ) {
        if rule.pattern.contains_self {
            // If the pattern contains `self` we restrict the scope of the search to just the
            // current method. No other method can reference the same `self`. This makes the
            // behavior of `self` consistent with other variables.
            if let Some(current_function) = self.resolution_scope.current_function() {
                self.slow_scan_node(&current_function, rule, &None, matches_out);
            }
            return;
        }
        if pick_path_for_usages(&rule.pattern).is_none() {
            self.slow_scan(rule, matches_out);
            return;
        }
        self.find_matches_for_pattern_tree(rule, &rule.pattern, usage_cache, matches_out);
    }

    fn find_matches_for_pattern_tree(
        &self,
        rule: &ResolvedRule,
        pattern: &ResolvedPattern,
        usage_cache: &mut UsageCache,
        matches_out: &mut Vec<Match>,
    ) {
        if let Some(resolved_path) = pick_path_for_usages(pattern) {
            let definition: Definition = resolved_path.resolution.clone().into();
            for file_range in self.find_usages(usage_cache, definition).file_ranges() {
                if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) {
                    if !is_search_permitted_ancestors(&node_to_match) {
                        cov_mark::hit!(use_declaration_with_braces);
                        continue;
                    }
                    self.try_add_match(rule, &node_to_match, &None, matches_out);
                }
            }
        }
    }

    fn find_node_to_match(
        &self,
        resolved_path: &ResolvedPath,
        file_range: FileRange,
    ) -> Option<SyntaxNode> {
        let file = self.sema.parse(file_range.file_id);
        let depth = resolved_path.depth as usize;
        let offset = file_range.range.start();
        if let Some(path) =
            self.sema.find_node_at_offset_with_descend::<ast::Path>(file.syntax(), offset)
        {
            self.sema.ancestors_with_macros(path.syntax().clone()).nth(depth)
        } else if let Some(path) =
            self.sema.find_node_at_offset_with_descend::<ast::MethodCallExpr>(file.syntax(), offset)
        {
            // If the pattern contained a path and we found a reference to that path that wasn't
            // itself a path, but was a method call, then we need to adjust how far up to try
            // matching by how deep the path was within a CallExpr. The structure would have been
            // CallExpr, PathExpr, Path - i.e. a depth offset of 2. We don't need to check if the
            // path was part of a CallExpr because if it wasn't then all that will happen is we'll
            // fail to match, which is the desired behavior.
            const PATH_DEPTH_IN_CALL_EXPR: usize = 2;
            if depth < PATH_DEPTH_IN_CALL_EXPR {
                return None;
            }
            self.sema
                .ancestors_with_macros(path.syntax().clone())
                .nth(depth - PATH_DEPTH_IN_CALL_EXPR)
        } else {
            None
        }
    }

    fn find_usages<'a>(
        &self,
        usage_cache: &'a mut UsageCache,
        definition: Definition,
    ) -> &'a UsageSearchResult {
        // Logically if a lookup succeeds we should just return it. Unfortunately returning it would
        // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a
        // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two
        // lookups in the case of a cache hit.
        if usage_cache.find(&definition).is_none() {
            let usages = definition.usages(&self.sema).in_scope(self.search_scope()).all();
            usage_cache.usages.push((definition, usages));
            return &usage_cache.usages.last().unwrap().1;
        }
        usage_cache.find(&definition).unwrap()
    }

    /// Returns the scope within which we want to search. We don't want un unrestricted search
    /// scope, since we don't want to find references in external dependencies.
    fn search_scope(&self) -> SearchScope {
        // FIXME: We should ideally have a test that checks that we edit local roots and not library
        // roots. This probably would require some changes to fixtures, since currently everything
        // seems to get put into a single source root.
        let mut files = Vec::new();
        self.search_files_do(|file_id| {
            files.push(file_id);
        });
        SearchScope::files(&files)
    }

    fn slow_scan(&self, rule: &ResolvedRule, matches_out: &mut Vec<Match>) {
        self.search_files_do(|file_id| {
            let file = self.sema.parse(file_id);
            let code = file.syntax();
            self.slow_scan_node(code, rule, &None, matches_out);
        })
    }

    fn search_files_do(&self, mut callback: impl FnMut(FileId)) {
        if self.restrict_ranges.is_empty() {
            // Unrestricted search.
            use ide_db::base_db::SourceDatabaseExt;
            use ide_db::symbol_index::SymbolsDatabase;
            for &root in self.sema.db.local_roots().iter() {
                let sr = self.sema.db.source_root(root);
                for file_id in sr.iter() {
                    callback(file_id);
                }
            }
        } else {
            // Search is restricted, deduplicate file IDs (generally only one).
            let mut files = FxHashSet::default();
            for range in &self.restrict_ranges {
                if files.insert(range.file_id) {
                    callback(range.file_id);
                }
            }
        }
    }

    fn slow_scan_node(
        &self,
        code: &SyntaxNode,
        rule: &ResolvedRule,
        restrict_range: &Option<FileRange>,
        matches_out: &mut Vec<Match>,
    ) {
        if !is_search_permitted(code) {
            return;
        }
        self.try_add_match(rule, code, restrict_range, matches_out);
        // If we've got a macro call, we already tried matching it pre-expansion, which is the only
        // way to match the whole macro, now try expanding it and matching the expansion.
        if let Some(macro_call) = ast::MacroCall::cast(code.clone()) {
            if let Some(expanded) = self.sema.expand(&macro_call) {
                if let Some(tt) = macro_call.token_tree() {
                    // When matching within a macro expansion, we only want to allow matches of
                    // nodes that originated entirely from within the token tree of the macro call.
                    // i.e. we don't want to match something that came from the macro itself.
                    self.slow_scan_node(
                        &expanded,
                        rule,
                        &Some(self.sema.original_range(tt.syntax())),
                        matches_out,
                    );
                }
            }
        }
        for child in code.children() {
            self.slow_scan_node(&child, rule, restrict_range, matches_out);
        }
    }

    fn try_add_match(
        &self,
        rule: &ResolvedRule,
        code: &SyntaxNode,
        restrict_range: &Option<FileRange>,
        matches_out: &mut Vec<Match>,
    ) {
        if !self.within_range_restrictions(code) {
            cov_mark::hit!(replace_nonpath_within_selection);
            return;
        }
        if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) {
            matches_out.push(m);
        }
    }

    /// Returns whether `code` is within one of our range restrictions if we have any. No range
    /// restrictions is considered unrestricted and always returns true.
    fn within_range_restrictions(&self, code: &SyntaxNode) -> bool {
        if self.restrict_ranges.is_empty() {
            // There is no range restriction.
            return true;
        }
        let node_range = self.sema.original_range(code);
        for range in &self.restrict_ranges {
            if range.file_id == node_range.file_id && range.range.contains_range(node_range.range) {
                return true;
            }
        }
        false
    }
}

/// Returns whether we support matching within `node` and all of its ancestors.
fn is_search_permitted_ancestors(node: &SyntaxNode) -> bool {
    if let Some(parent) = node.parent() {
        if !is_search_permitted_ancestors(&parent) {
            return false;
        }
    }
    is_search_permitted(node)
}

/// Returns whether we support matching within this kind of node.
fn is_search_permitted(node: &SyntaxNode) -> bool {
    // FIXME: Properly handle use declarations. At the moment, if our search pattern is `foo::bar`
    // and the code is `use foo::{baz, bar}`, we'll match `bar`, since it resolves to `foo::bar`.
    // However we'll then replace just the part we matched `bar`. We probably need to instead remove
    // `bar` and insert a new use declaration.
    node.kind() != SyntaxKind::USE
}

impl UsageCache {
    fn find(&mut self, definition: &Definition) -> Option<&UsageSearchResult> {
        // We expect a very small number of cache entries (generally 1), so a linear scan should be
        // fast enough and avoids the need to implement Hash for Definition.
        for (d, refs) in &self.usages {
            if d == definition {
                return Some(refs);
            }
        }
        None
    }
}

/// Returns a path that's suitable for path resolution. We exclude builtin types, since they aren't
/// something that we can find references to. We then somewhat arbitrarily pick the path that is the
/// longest as this is hopefully more likely to be less common, making it faster to find.
fn pick_path_for_usages(pattern: &ResolvedPattern) -> Option<&ResolvedPath> {
    // FIXME: Take the scope of the resolved path into account. e.g. if there are any paths that are
    // private to the current module, then we definitely would want to pick them over say a path
    // from std. Possibly we should go further than this and intersect the search scopes for all
    // resolved paths then search only in that scope.
    pattern
        .resolved_paths
        .iter()
        .filter(|(_, p)| {
            !matches!(p.resolution, hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_)))
        })
        .map(|(node, resolved)| (node.text().len(), resolved))
        .max_by(|(a, _), (b, _)| a.cmp(b))
        .map(|(_, resolved)| resolved)
}
<commit_msg>maybe?<commit_after>//! Searching for matches.

use crate::{
    matching,
    resolving::{ResolvedPath, ResolvedPattern, ResolvedRule},
    Match, MatchFinder,
};
use ide_db::{
    base_db::{FileId, FileRange},
    defs::Definition,
    search::{SearchScope, UsageSearchResult},
};
use rustc_hash::FxHashSet;
use syntax::{ast, AstNode, SyntaxKind, SyntaxNode};

/// A cache for the results of find_usages. This is for when we have multiple patterns that have the
/// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type
/// and as a pattern. In each, the usages of `foo::Bar` are the same and we'd like to avoid finding
/// them more than once.
#[derive(Default)]
pub(crate) struct UsageCache {
    usages: Vec<(Definition, UsageSearchResult)>,
}

impl<'db> MatchFinder<'db> {
    /// Adds all matches for `rule` to `matches_out`. Matches may overlap in ways that make
    /// replacement impossible, so further processing is required in order to properly nest matches
    /// and remove overlapping matches. This is done in the `nesting` module.
    pub(crate) fn find_matches_for_rule(
        &self,
        rule: &ResolvedRule,
        usage_cache: &mut UsageCache,
        matches_out: &mut Vec<Match>,
    ) {
        if rule.pattern.contains_self {
            // If the pattern contains `self` we restrict the scope of the search to just the
            // current method. No other method can reference the same `self`. This makes the
            // behavior of `self` consistent with other variables.
            if let Some(current_function) = self.resolution_scope.current_function() {
                self.slow_scan_node(&current_function, rule, &None, matches_out);
            }
            return;
        }
        if pick_path_for_usages(&rule.pattern).is_none() {
            self.slow_scan(rule, matches_out);
            return;
        }
        self.find_matches_for_pattern_tree(rule, &rule.pattern, usage_cache, matches_out);
    }

    fn find_matches_for_pattern_tree(
        &self,
        rule: &ResolvedRule,
        pattern: &ResolvedPattern,
        usage_cache: &mut UsageCache,
        matches_out: &mut Vec<Match>,
    ) {
        if let Some(resolved_path) = pick_path_for_usages(pattern) {
            let definition: Definition = resolved_path.resolution.clone().into();
            for file_range in self.find_usages(usage_cache, definition).file_ranges() {
                for node_to_match in self.find_nodes_to_match(resolved_path, file_range) {
                    if !is_search_permitted_ancestors(&node_to_match) {
                        cov_mark::hit!(use_declaration_with_braces);
                        continue;
                    }
                    self.try_add_match(rule, &node_to_match, &None, matches_out);
                }
            }
        }
    }

    fn find_nodes_to_match(
        &self,
        resolved_path: &ResolvedPath,
        file_range: FileRange,
    ) -> Vec<SyntaxNode> {
        let file = self.sema.parse(file_range.file_id);
        let depth = resolved_path.depth as usize;
        let offset = file_range.range.start();

        let mut paths = self
            .sema
            .find_nodes_at_offset_with_descend::<ast::Path>(file.syntax(), offset)
            .peekable();

        if paths.peek().is_some() {
            paths
                .filter_map(|path| {
                    self.sema.ancestors_with_macros(path.syntax().clone()).nth(depth)
                })
                .collect::<Vec<_>>()
        } else {
            self.sema
                .find_nodes_at_offset_with_descend::<ast::MethodCallExpr>(file.syntax(), offset)
                .filter_map(|path| {
                    // If the pattern contained a path and we found a reference to that path that wasn't
                    // itself a path, but was a method call, then we need to adjust how far up to try
                    // matching by how deep the path was within a CallExpr. The structure would have been
                    // CallExpr, PathExpr, Path - i.e. a depth offset of 2. We don't need to check if the
                    // path was part of a CallExpr because if it wasn't then all that will happen is we'll
                    // fail to match, which is the desired behavior.
                    const PATH_DEPTH_IN_CALL_EXPR: usize = 2;
                    if depth < PATH_DEPTH_IN_CALL_EXPR {
                        return None;
                    }
                    self.sema
                        .ancestors_with_macros(path.syntax().clone())
                        .nth(depth - PATH_DEPTH_IN_CALL_EXPR)
                })
                .collect::<Vec<_>>()
        }
    }

    fn find_usages<'a>(
        &self,
        usage_cache: &'a mut UsageCache,
        definition: Definition,
    ) -> &'a UsageSearchResult {
        // Logically if a lookup succeeds we should just return it. Unfortunately returning it would
        // extend the lifetime of the borrow, then we wouldn't be able to do the insertion on a
        // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two
        // lookups in the case of a cache hit.
        if usage_cache.find(&definition).is_none() {
            let usages = definition.usages(&self.sema).in_scope(self.search_scope()).all();
            usage_cache.usages.push((definition, usages));
            return &usage_cache.usages.last().unwrap().1;
        }
        usage_cache.find(&definition).unwrap()
    }

    /// Returns the scope within which we want to search. We don't want un unrestricted search
    /// scope, since we don't want to find references in external dependencies.
    fn search_scope(&self) -> SearchScope {
        // FIXME: We should ideally have a test that checks that we edit local roots and not library
        // roots. This probably would require some changes to fixtures, since currently everything
        // seems to get put into a single source root.
        let mut files = Vec::new();
        self.search_files_do(|file_id| {
            files.push(file_id);
        });
        SearchScope::files(&files)
    }

    fn slow_scan(&self, rule: &ResolvedRule, matches_out: &mut Vec<Match>) {
        self.search_files_do(|file_id| {
            let file = self.sema.parse(file_id);
            let code = file.syntax();
            self.slow_scan_node(code, rule, &None, matches_out);
        })
    }

    fn search_files_do(&self, mut callback: impl FnMut(FileId)) {
        if self.restrict_ranges.is_empty() {
            // Unrestricted search.
            use ide_db::base_db::SourceDatabaseExt;
            use ide_db::symbol_index::SymbolsDatabase;
            for &root in self.sema.db.local_roots().iter() {
                let sr = self.sema.db.source_root(root);
                for file_id in sr.iter() {
                    callback(file_id);
                }
            }
        } else {
            // Search is restricted, deduplicate file IDs (generally only one).
            let mut files = FxHashSet::default();
            for range in &self.restrict_ranges {
                if files.insert(range.file_id) {
                    callback(range.file_id);
                }
            }
        }
    }

    fn slow_scan_node(
        &self,
        code: &SyntaxNode,
        rule: &ResolvedRule,
        restrict_range: &Option<FileRange>,
        matches_out: &mut Vec<Match>,
    ) {
        if !is_search_permitted(code) {
            return;
        }
        self.try_add_match(rule, code, restrict_range, matches_out);
        // If we've got a macro call, we already tried matching it pre-expansion, which is the only
        // way to match the whole macro, now try expanding it and matching the expansion.
        if let Some(macro_call) = ast::MacroCall::cast(code.clone()) {
            if let Some(expanded) = self.sema.expand(&macro_call) {
                if let Some(tt) = macro_call.token_tree() {
                    // When matching within a macro expansion, we only want to allow matches of
                    // nodes that originated entirely from within the token tree of the macro call.
                    // i.e. we don't want to match something that came from the macro itself.
                    self.slow_scan_node(
                        &expanded,
                        rule,
                        &Some(self.sema.original_range(tt.syntax())),
                        matches_out,
                    );
                }
            }
        }
        for child in code.children() {
            self.slow_scan_node(&child, rule, restrict_range, matches_out);
        }
    }

    fn try_add_match(
        &self,
        rule: &ResolvedRule,
        code: &SyntaxNode,
        restrict_range: &Option<FileRange>,
        matches_out: &mut Vec<Match>,
    ) {
        if !self.within_range_restrictions(code) {
            cov_mark::hit!(replace_nonpath_within_selection);
            return;
        }
        if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) {
            matches_out.push(m);
        }
    }

    /// Returns whether `code` is within one of our range restrictions if we have any. No range
    /// restrictions is considered unrestricted and always returns true.
    fn within_range_restrictions(&self, code: &SyntaxNode) -> bool {
        if self.restrict_ranges.is_empty() {
            // There is no range restriction.
            return true;
        }
        let node_range = self.sema.original_range(code);
        for range in &self.restrict_ranges {
            if range.file_id == node_range.file_id && range.range.contains_range(node_range.range) {
                return true;
            }
        }
        false
    }
}

/// Returns whether we support matching within `node` and all of its ancestors.
fn is_search_permitted_ancestors(node: &SyntaxNode) -> bool {
    if let Some(parent) = node.parent() {
        if !is_search_permitted_ancestors(&parent) {
            return false;
        }
    }
    is_search_permitted(node)
}

/// Returns whether we support matching within this kind of node.
fn is_search_permitted(node: &SyntaxNode) -> bool {
    // FIXME: Properly handle use declarations. At the moment, if our search pattern is `foo::bar`
    // and the code is `use foo::{baz, bar}`, we'll match `bar`, since it resolves to `foo::bar`.
    // However we'll then replace just the part we matched `bar`. We probably need to instead remove
    // `bar` and insert a new use declaration.
    node.kind() != SyntaxKind::USE
}

impl UsageCache {
    fn find(&mut self, definition: &Definition) -> Option<&UsageSearchResult> {
        // We expect a very small number of cache entries (generally 1), so a linear scan should be
        // fast enough and avoids the need to implement Hash for Definition.
        for (d, refs) in &self.usages {
            if d == definition {
                return Some(refs);
            }
        }
        None
    }
}

/// Returns a path that's suitable for path resolution. We exclude builtin types, since they aren't
/// something that we can find references to. We then somewhat arbitrarily pick the path that is the
/// longest as this is hopefully more likely to be less common, making it faster to find.
fn pick_path_for_usages(pattern: &ResolvedPattern) -> Option<&ResolvedPath> {
    // FIXME: Take the scope of the resolved path into account. e.g. if there are any paths that are
    // private to the current module, then we definitely would want to pick them over say a path
    // from std. Possibly we should go further than this and intersect the search scopes for all
    // resolved paths then search only in that scope.
    pattern
        .resolved_paths
        .iter()
        .filter(|(_, p)| {
            !matches!(p.resolution, hir::PathResolution::Def(hir::ModuleDef::BuiltinType(_)))
        })
        .map(|(node, resolved)| (node.text().len(), resolved))
        .max_by(|(a, _), (b, _)| a.cmp(b))
        .map(|(_, resolved)| resolved)
}
<|endoftext|>


<commit_before>use super::generate;

#[test]
fn partial_overlap() {
    let left  = generate(vec![('3', '5'), ('4', '6')]);
    let right = generate(vec![('3', '5'), ('1', '4')]);

    let other_left  = generate(vec![('3', '6')]);
    let other_right = generate(vec![('1', '5')]);

    assert_eq!(left, other_left);
    assert_eq!(right, other_right);
}
#[test]
fn subset() {
    let inner = generate(vec![('1', '5'), ('2', '3')]);
    let left  = generate(vec![('1', '5'), ('1', '3')]);
    let right = generate(vec![('1', '5'), ('2', '5')]);
    let both  = generate(vec![('1', '5'), ('1', '5')]);

    let other = generate(vec![('1', '5')]);

    assert_eq!(inner, other);
    assert_eq!(left, other);
    assert_eq!(right, other);
    assert_eq!(both, other);
}
#[test]
fn superset() {
    let set   = generate(vec![('3', '5'), ('2', '6')]);
    let other = generate(vec![('2', '6')]);

    assert_eq!(other, set);
}
#[test]
// Useless test but added for completeness.
fn disjoint() {
    let set   = generate(vec![('2', '4'), ('6', '8')]);
    let other = generate(vec![('2', '4'), ('6', '8')]);

    assert_eq!(other, set);
}
#[test]
fn disjoint_extend() {
    let left  = generate(vec![('5', '8'), ('2', '4')]);
    let right = generate(vec![('2', '4'), ('5', '8')]);
    let other = generate(vec![('2', '8')]);

    assert_eq!(other, left);
    assert_eq!(other, right);
}
#[test]
#[should_panic]
fn panic_on_decreasing_order() {
    generate(vec![('5', '3')]);
}
<commit_msg>Tweak insert tests<commit_after>use super::generate;

#[test]
fn partial_overlap() {
    let left  = generate(vec![('4', '6'), ('3', '4')]);
    let right = generate(vec![('3', '4'), ('4', '6')]);

    let other  = generate(vec![('3', '6')]);

    assert_eq!(left, other);
    assert_eq!(right, other);
}
#[test]
fn subset() {
    let inner = generate(vec![('1', '5'), ('2', '3')]);
    let left  = generate(vec![('1', '5'), ('1', '3')]);
    let right = generate(vec![('1', '5'), ('2', '5')]);
    let both  = generate(vec![('1', '5'), ('1', '5')]);

    let other = generate(vec![('1', '5')]);

    assert_eq!(inner, other);
    assert_eq!(left, other);
    assert_eq!(right, other);
    assert_eq!(both, other);
}
#[test]
fn superset() {
    let set   = generate(vec![('3', '5'), ('2', '6')]);
    let other = generate(vec![('2', '6')]);

    assert_eq!(other, set);
}
#[test]
fn disjoint() {
    let set   = generate(vec![('2', '4'), ('6', '8')]);
    let other = generate(vec![('6', '8'), ('2', '4')]);

    assert_eq!(other, set);
}
#[test]
fn disjoint_extend() {
    let left  = generate(vec![('5', '8'), ('2', '4')]);
    let right = generate(vec![('2', '4'), ('5', '8')]);
    let other = generate(vec![('2', '8')]);

    assert_eq!(other, left);
    assert_eq!(other, right);
}
#[test]
#[should_panic]
fn panic_on_decreasing_order() {
    generate(vec![('5', '3')]);
}
<|endoftext|>


<commit_before>use crate::helper::{load_module_test, run_top_level_test_with_args};
use serial_test::serial;

#[serial]
#[test]
fn builtins() {
    load_module_test("src/tests/builtins.pl", "");
}

#[serial]
#[test]
fn call_with_inference_limit() {
    load_module_test("src/tests/call_with_inference_limit.pl", "");
}

#[serial]
#[test]
fn facts() {
    load_module_test("src/tests/facts.pl", "");
}

#[serial]
#[test]
fn hello_world() {
    load_module_test("src/tests/hello_world.pl", "Hello World!\n");
}

#[serial]
#[test]
fn syntax_error() {
    load_module_test(
        "tests-pl/syntax_error.pl",
        "caught: error(syntax_error(incomplete_reduction),read_term/3:6)\n",
    );
}

#[serial]
#[test]
fn predicates() {
    load_module_test("src/tests/predicates.pl", "");
}

#[serial]
#[test]
fn rules() {
    load_module_test("src/tests/rules.pl", "");
}

#[serial]
#[test]
fn setup_call_cleanup_load() {
    load_module_test(
        "src/tests/setup_call_cleanup.pl",
        "1+21+31+2>_15785+_157861+_142871+2>41+2>_157861+2>31+2>31+2>4ba"
    );
}

#[test]
fn setup_call_cleanup_process() {
    run_top_level_test_with_args(
        &["src/tests/setup_call_cleanup.pl", "-f", "-g", "halt"],
        "",
        "1+21+31+2>_17278+_172791+_157801+2>41+2>_172791+2>31+2>31+2>4ba"
    );
}

#[serial]
#[test]
fn clpz_load() {
    load_module_test("src/tests/clpz/test_clpz.pl", "");
}
<commit_msg>update setup_call_cleanup tests<commit_after>use crate::helper::{load_module_test, run_top_level_test_with_args};
use serial_test::serial;

#[serial]
#[test]
fn builtins() {
    load_module_test("src/tests/builtins.pl", "");
}

#[serial]
#[test]
fn call_with_inference_limit() {
    load_module_test("src/tests/call_with_inference_limit.pl", "");
}

#[serial]
#[test]
fn facts() {
    load_module_test("src/tests/facts.pl", "");
}

#[serial]
#[test]
fn hello_world() {
    load_module_test("src/tests/hello_world.pl", "Hello World!\n");
}

#[serial]
#[test]
fn syntax_error() {
    load_module_test(
        "tests-pl/syntax_error.pl",
        "caught: error(syntax_error(incomplete_reduction),read_term/3:6)\n",
    );
}

#[serial]
#[test]
fn predicates() {
    load_module_test("src/tests/predicates.pl", "");
}

#[serial]
#[test]
fn rules() {
    load_module_test("src/tests/rules.pl", "");
}

#[serial]
#[test]
fn setup_call_cleanup_load() {
    load_module_test(
        "src/tests/setup_call_cleanup.pl",
        "1+21+31+2>_17737+_177381+_158071+2>41+2>_177381+2>31+2>31+2>4ba"
    );
}

#[test]
fn setup_call_cleanup_process() {
    run_top_level_test_with_args(
        &["src/tests/setup_call_cleanup.pl", "-f", "-g", "halt"],
        "",
        "1+21+31+2>_19590+_195911+_176601+2>41+2>_195911+2>31+2>31+2>4ba"
    );
}

#[serial]
#[test]
fn clpz_load() {
    load_module_test("src/tests/clpz/test_clpz.pl", "");
}
<|endoftext|>


<commit_before>use super::*;

pub(super) fn mod_contents(p: &mut Parser) {
    attributes::inner_attributes(p);
    while !p.at(EOF) {
        item(p);
    }
}

pub(super) const ITEM_FIRST: TokenSet = token_set![
    EXTERN_KW,
    MOD_KW,
    USE_KW,
    STRUCT_KW,
    FN_KW,
    PUB_KW,
    POUND,
];

fn item(p: &mut Parser) {
    let item = p.start();
    attributes::outer_attributes(p);
    visibility(p);
    let la = p.raw_lookahead(1);
    let item_kind = match p.current() {
        EXTERN_KW if la == CRATE_KW => {
            extern_crate_item(p);
            EXTERN_CRATE_ITEM
        }
        MOD_KW => {
            mod_item(p);
            MOD_ITEM
        }
        USE_KW => {
            use_item(p);
            USE_ITEM
        }
        STRUCT_KW => {
            struct_item(p);
            STRUCT_ITEM
        }
        FN_KW => {
            fn_item(p);
            FN_ITEM
        }
        err_token => {
            item.abandon(p);
            let err = p.start();
            let message = if err_token == SEMI {
                //TODO: if the item is incomplete, this message is misleading
                "expected item, found `;`\n\
                consider removing this semicolon"
            } else {
                "expected item"
            };
            p.error()
                .message(message)
                .emit();
            p.bump();
            err.complete(p, ERROR);
            return;
        }
    };
    item.complete(p, item_kind);
}

fn struct_item(p: &mut Parser) {
    assert!(p.at(STRUCT_KW));
    p.bump();

    if !p.expect(IDENT) {
        return;
    }
    generic_parameters(p);
    match p.current() {
        WHERE_KW => {
            where_clause(p);
            match p.current() {
                SEMI => {
                    p.bump();
                    return;
                }
                L_CURLY => named_fields(p),
                _ => { //TODO: special case `(` error message
                    p.error()
                        .message("expected `;` or `{`")
                        .emit();
                    return;
                }
            }
        }
        SEMI => {
            p.bump();
            return;
        }
        L_CURLY => named_fields(p),
        L_PAREN => {
            pos_fields(p);
            p.expect(SEMI);
        }
        _ => {
            p.error()
                .message("expected `;`, `{`, or `(`")
                .emit();
            return;
        }
    }
}

fn named_fields(p: &mut Parser) {
    p.curly_block(|p| comma_list(p, EOF, |p| {
        named_field(p);
        true
    }));

    fn named_field(p: &mut Parser) {
        let field = p.start();
        visibility(p);
        if p.expect(IDENT) && p.expect(COLON) {
            types::type_ref(p);
        };
        field.complete(p, NAMED_FIELD);
    }
}

fn pos_fields(p: &mut Parser) {
    if !p.expect(L_PAREN) {
        return;
    }
    loop {
        if p.at(R_PAREN) || p.at(EOF) {
            break;
        }

        let pos_field = p.start();
        visibility(p);
        types::type_ref(p);
        pos_field.complete(p, POS_FIELD);

        if !p.at(R_PAREN) {
            p.expect(COMMA);
        }
    }
    p.expect(R_PAREN);
}

fn generic_parameters(_: &mut Parser) {}

fn where_clause(_: &mut Parser) {}

fn extern_crate_item(p: &mut Parser) {
    assert!(p.at(EXTERN_KW));
    p.bump();
    assert!(p.at(CRATE_KW));
    p.bump();

    p.expect(IDENT) && alias(p) && p.expect(SEMI);
}

fn mod_item(p: &mut Parser) {
    assert!(p.at(MOD_KW));
    p.bump();

    if p.expect(IDENT) && !p.eat(SEMI) {
        p.curly_block(mod_contents);
    }
}

pub(super) fn is_use_tree_start(kind: SyntaxKind) -> bool {
    kind == STAR || kind == L_CURLY
}

fn use_item(p: &mut Parser) {
    assert!(p.at(USE_KW));
    p.bump();

    use_tree(p);
    p.expect(SEMI);

    fn use_tree(p: &mut Parser) -> bool {
        let la = p.raw_lookahead(1);
        let m = p.start();
        match (p.current(), la) {
            (STAR, _) => {
                p.bump();
            }
            (COLONCOLON, STAR) => {
                p.bump();
                p.bump();
            }
            (L_CURLY, _) | (COLONCOLON, L_CURLY) => {
                if p.at(COLONCOLON) {
                    p.bump();
                }
                nested_trees(p);
            }
            _ if paths::is_path_start(p) => {
                paths::use_path(p);
                match p.current() {
                    AS_KW => {
                        alias(p);
                    }
                    COLONCOLON => {
                        p.bump();
                        match p.current() {
                            STAR => {
                                p.bump();
                            }
                            L_CURLY => nested_trees(p),
                            _ => {
                                // is this unreachable?
                                p.error()
                                    .message("expected `{` or `*`")
                                    .emit();
                            }
                        }
                    }
                    _ => (),
                }
            }
            _ => {
                m.abandon(p);
                return false
            },
        }
        m.complete(p, USE_TREE);
        return true;
    }

    fn nested_trees(p: &mut Parser) {
        assert!(p.at(L_CURLY));
        p.curly_block(|p| comma_list(p, EOF, use_tree));
    }
}

fn fn_item(p: &mut Parser) {
    assert!(p.at(FN_KW));
    p.bump();

    p.expect(IDENT) && p.expect(L_PAREN) && p.expect(R_PAREN)
        && p.curly_block(|_| ());
}

<commit_msg>Simplify<commit_after>use super::*;

pub(super) fn mod_contents(p: &mut Parser) {
    attributes::inner_attributes(p);
    while !p.at(EOF) {
        item(p);
    }
}

pub(super) const ITEM_FIRST: TokenSet = token_set![
    EXTERN_KW,
    MOD_KW,
    USE_KW,
    STRUCT_KW,
    FN_KW,
    PUB_KW,
    POUND,
];

fn item(p: &mut Parser) {
    let item = p.start();
    attributes::outer_attributes(p);
    visibility(p);
    let la = p.raw_lookahead(1);
    let item_kind = match p.current() {
        EXTERN_KW if la == CRATE_KW => {
            extern_crate_item(p);
            EXTERN_CRATE_ITEM
        }
        MOD_KW => {
            mod_item(p);
            MOD_ITEM
        }
        USE_KW => {
            use_item(p);
            USE_ITEM
        }
        STRUCT_KW => {
            struct_item(p);
            STRUCT_ITEM
        }
        FN_KW => {
            fn_item(p);
            FN_ITEM
        }
        err_token => {
            item.abandon(p);
            let err = p.start();
            let message = if err_token == SEMI {
                //TODO: if the item is incomplete, this message is misleading
                "expected item, found `;`\n\
                consider removing this semicolon"
            } else {
                "expected item"
            };
            p.error()
                .message(message)
                .emit();
            p.bump();
            err.complete(p, ERROR);
            return;
        }
    };
    item.complete(p, item_kind);
}

fn struct_item(p: &mut Parser) {
    assert!(p.at(STRUCT_KW));
    p.bump();

    if !p.expect(IDENT) {
        return;
    }
    generic_parameters(p);
    match p.current() {
        WHERE_KW => {
            where_clause(p);
            match p.current() {
                SEMI => {
                    p.bump();
                    return;
                }
                L_CURLY => named_fields(p),
                _ => { //TODO: special case `(` error message
                    p.error()
                        .message("expected `;` or `{`")
                        .emit();
                    return;
                }
            }
        }
        SEMI => {
            p.bump();
            return;
        }
        L_CURLY => named_fields(p),
        L_PAREN => {
            pos_fields(p);
            p.expect(SEMI);
        }
        _ => {
            p.error()
                .message("expected `;`, `{`, or `(`")
                .emit();
            return;
        }
    }
}

fn named_fields(p: &mut Parser) {
    p.curly_block(|p| comma_list(p, EOF, |p| {
        named_field(p);
        true
    }));

    fn named_field(p: &mut Parser) {
        let field = p.start();
        visibility(p);
        if p.expect(IDENT) && p.expect(COLON) {
            types::type_ref(p);
        };
        field.complete(p, NAMED_FIELD);
    }
}

fn pos_fields(p: &mut Parser) {
    if !p.expect(L_PAREN) {
        return;
    }
    while !(p.at(R_PAREN) || p.at(EOF)) {
        let pos_field = p.start();
        visibility(p);
        types::type_ref(p);
        pos_field.complete(p, POS_FIELD);

        if !p.at(R_PAREN) {
            p.expect(COMMA);
        }
    }
    p.expect(R_PAREN);
}

fn generic_parameters(_: &mut Parser) {}

fn where_clause(_: &mut Parser) {}

fn extern_crate_item(p: &mut Parser) {
    assert!(p.at(EXTERN_KW));
    p.bump();
    assert!(p.at(CRATE_KW));
    p.bump();

    p.expect(IDENT) && alias(p) && p.expect(SEMI);
}

fn mod_item(p: &mut Parser) {
    assert!(p.at(MOD_KW));
    p.bump();

    if p.expect(IDENT) && !p.eat(SEMI) {
        p.curly_block(mod_contents);
    }
}

pub(super) fn is_use_tree_start(kind: SyntaxKind) -> bool {
    kind == STAR || kind == L_CURLY
}

fn use_item(p: &mut Parser) {
    assert!(p.at(USE_KW));
    p.bump();

    use_tree(p);
    p.expect(SEMI);

    fn use_tree(p: &mut Parser) -> bool {
        let la = p.raw_lookahead(1);
        let m = p.start();
        match (p.current(), la) {
            (STAR, _) => {
                p.bump();
            }
            (COLONCOLON, STAR) => {
                p.bump();
                p.bump();
            }
            (L_CURLY, _) | (COLONCOLON, L_CURLY) => {
                if p.at(COLONCOLON) {
                    p.bump();
                }
                nested_trees(p);
            }
            _ if paths::is_path_start(p) => {
                paths::use_path(p);
                match p.current() {
                    AS_KW => {
                        alias(p);
                    }
                    COLONCOLON => {
                        p.bump();
                        match p.current() {
                            STAR => {
                                p.bump();
                            }
                            L_CURLY => nested_trees(p),
                            _ => {
                                // is this unreachable?
                                p.error()
                                    .message("expected `{` or `*`")
                                    .emit();
                            }
                        }
                    }
                    _ => (),
                }
            }
            _ => {
                m.abandon(p);
                return false
            },
        }
        m.complete(p, USE_TREE);
        return true;
    }

    fn nested_trees(p: &mut Parser) {
        assert!(p.at(L_CURLY));
        p.curly_block(|p| comma_list(p, EOF, use_tree));
    }
}

fn fn_item(p: &mut Parser) {
    assert!(p.at(FN_KW));
    p.bump();

    p.expect(IDENT) && p.expect(L_PAREN) && p.expect(R_PAREN)
        && p.curly_block(|_| ());
}

<|endoftext|>


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

use cssparser;
use media_queries::CSSErrorReporterTest;
use selectors::parser::*;
use std::borrow::ToOwned;
use std::sync::Arc;
use string_cache::Atom;
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
use style::stylesheets::{CSSRule, StyleRule, Origin, Stylesheet};

#[test]
fn test_parse_stylesheet() {
    let css = r"
        @namespace url(http://www.w3.org/1999/xhtml);
        /* FIXME: only if scripting is enabled */
        input[type=hidden i] { display: none !important; }
        html , body /**/ { display: block; }
        #d1 > .ok { background: blue; }
    ";
    let url = url!("about::test");
    let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
                                          Box::new(CSSErrorReporterTest));
    assert_eq!(stylesheet, Stylesheet {
        origin: Origin::UserAgent,
        media: None,
        rules: vec![
            CSSRule::Namespace(None, ns!(html)),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(html)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!("input"),
                                    lower_name: atom!("input"),
                                }),
                                SimpleSelector::AttrEqual(AttrSelector {
                                    name: atom!("type"),
                                    lower_name: atom!("type"),
                                    namespace: NamespaceConstraint::Specific(ns!()),
                                }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (1 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![]),
                    important: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::Value(
                            longhands::display::SpecifiedValue::none)),
                    ]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(html)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!("html"),
                                    lower_name: atom!("html"),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(html)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!("body"),
                                    lower_name: atom!("body"),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::Value(
                            longhands::display::SpecifiedValue::block)),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Class(Atom::from("ok")),
                            ],
                            next: Some((Arc::new(CompoundSelector {
                                simple_selectors: vec![
                                    SimpleSelector::ID(Atom::from("d1")),
                                ],
                                next: None,
                            }), Combinator::Child)),
                        }),
                        pseudo_element: None,
                        specificity: (1 << 20) + (1 << 10) + (0 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::BackgroundClip(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundOrigin(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
                            longhands::background_color::SpecifiedValue {
                                authored: Some("blue".to_owned()),
                                parsed: cssparser::Color::RGBA(cssparser::RGBA {
                                    red: 0., green: 0., blue: 1., alpha: 1.
                                }),
                            }
                        )),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
        ],
    });
}
<commit_msg>Added test to verify parse error reporter is functioning<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cssparser::{self, Parser, SourcePosition};
use media_queries::CSSErrorReporterTest;
use selectors::parser::*;
use std::borrow::ToOwned;
use std::sync::Arc;
use std::sync::Mutex;
use string_cache::Atom;
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, DeclaredValue, longhands};
use style::stylesheets::{CSSRule, StyleRule, Origin, Stylesheet};
use style::error_reporting::ParseErrorReporter;

#[test]
fn test_parse_stylesheet() {
    let css = r"
        @namespace url(http://www.w3.org/1999/xhtml);
        /* FIXME: only if scripting is enabled */
        input[type=hidden i] { display: none !important; }
        html , body /**/ { display: block; }
        #d1 > .ok { background: blue; }
    ";
    let url = url!("about::test");
    let stylesheet = Stylesheet::from_str(css, url, Origin::UserAgent,
                                          Box::new(CSSErrorReporterTest));
    assert_eq!(stylesheet, Stylesheet {
        origin: Origin::UserAgent,
        media: None,
        rules: vec![
            CSSRule::Namespace(None, ns!(html)),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(html)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!("input"),
                                    lower_name: atom!("input"),
                                }),
                                SimpleSelector::AttrEqual(AttrSelector {
                                    name: atom!("type"),
                                    lower_name: atom!("type"),
                                    namespace: NamespaceConstraint::Specific(ns!()),
                                }, "hidden".to_owned(), CaseSensitivity::CaseInsensitive)
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (1 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![]),
                    important: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::Value(
                            longhands::display::SpecifiedValue::none)),
                    ]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(html)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!("html"),
                                    lower_name: atom!("html"),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Namespace(ns!(html)),
                                SimpleSelector::LocalName(LocalName {
                                    name: atom!("body"),
                                    lower_name: atom!("body"),
                                }),
                            ],
                            next: None,
                        }),
                        pseudo_element: None,
                        specificity: (0 << 20) + (0 << 10) + (1 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::Display(DeclaredValue::Value(
                            longhands::display::SpecifiedValue::block)),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
            CSSRule::Style(StyleRule {
                selectors: vec![
                    Selector {
                        compound_selectors: Arc::new(CompoundSelector {
                            simple_selectors: vec![
                                SimpleSelector::Class(Atom::from("ok")),
                            ],
                            next: Some((Arc::new(CompoundSelector {
                                simple_selectors: vec![
                                    SimpleSelector::ID(Atom::from("d1")),
                                ],
                                next: None,
                            }), Combinator::Child)),
                        }),
                        pseudo_element: None,
                        specificity: (1 << 20) + (1 << 10) + (0 << 0),
                    },
                ],
                declarations: PropertyDeclarationBlock {
                    normal: Arc::new(vec![
                        PropertyDeclaration::BackgroundClip(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundOrigin(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundSize(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundImage(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundPosition(DeclaredValue::Initial),
                        PropertyDeclaration::BackgroundColor(DeclaredValue::Value(
                            longhands::background_color::SpecifiedValue {
                                authored: Some("blue".to_owned()),
                                parsed: cssparser::Color::RGBA(cssparser::RGBA {
                                    red: 0., green: 0., blue: 1., alpha: 1.
                                }),
                            }
                        )),
                    ]),
                    important: Arc::new(vec![]),
                },
            }),
        ],
    });
}

struct CSSError {
    pub line: usize,
    pub column: usize,
    pub message: String
}

struct CSSInvalidErrorReporterTest {
    pub errors: Arc<Mutex<Vec<CSSError>>>
}

impl CSSInvalidErrorReporterTest {
    pub fn new() -> CSSInvalidErrorReporterTest {
        return CSSInvalidErrorReporterTest{
            errors: Arc::new(Mutex::new(Vec::new()))
        }
    }
}

impl ParseErrorReporter for CSSInvalidErrorReporterTest {
    fn report_error(&self, input: &mut Parser, position: SourcePosition, message: &str) {
        let location = input.source_location(position);

        let errors = self.errors.clone();
        let mut errors = errors.lock().unwrap();

        errors.push(
            CSSError{
                line: location.line,
                column: location.column,
                message: message.to_owned()
            }
        );
    }

    fn clone(&self) -> Box<ParseErrorReporter + Send + Sync> {
        return Box::new(
            CSSInvalidErrorReporterTest{
                errors: self.errors.clone()
            }
        );
    }
}

#[test]
fn test_report_error_stylesheet() {
    let css = r"
    div {
        background-color: red;
        display: invalid;
        invalid: true;
    }
    ";
    let url = url!("about::test");
    let error_reporter = Box::new(CSSInvalidErrorReporterTest::new());

    let errors = error_reporter.errors.clone();

    Stylesheet::from_str(css, url, Origin::UserAgent, error_reporter);

    let mut errors = errors.lock().unwrap();

    let error = errors.pop().unwrap();
    assert_eq!("Unsupported property declaration: 'invalid: true;'", error.message);
    assert_eq!(5, error.line);
    assert_eq!(9, error.column);

    let error = errors.pop().unwrap();
    assert_eq!("Unsupported property declaration: 'display: invalid;'", error.message);
    assert_eq!(4, error.line);
    assert_eq!(9, error.column);
}
<|endoftext|>