<commit_before>use std::io::BufferedReader;
use std::io::File;
use std::io::fs::PathExtensions;
use std::io::process::Command;

use waypoint_generator::WaypointGenerator;

/**
 * Loads and returns waypoints from a KML file.
 */
struct KmlWaypointGenerator {
    waypoints: Vec<(f32, f32)>,
    current_waypoint: u32,
}

impl KmlWaypointGenerator {
    pub fn new(file_name: &str) -> KmlWaypointGenerator {
        KmlWaypointGenerator {
            waypoints: KmlWaypointGenerator::load_waypoints(file_name),
            current_waypoint: 0
        }
    }

    fn load_waypoints(file_name: &str) -> Vec<(f32, f32)> {
        let path = Path::new(file_name);
        if !path.exists() || !path.is_file() {
            fail!("File does not exist: {}", file_name);
        }

        // A KML file is a zip archive containing a single file named "doc.kml"
        // that is an XML file
        let temp_directory = "/tmp/waypoints";
        let zip_io_result = Command::new("unzip")
            .arg(file_name)
            .arg("-d")  // Output directory
            .arg(temp_directory)
            .spawn();
        match zip_io_result {
            Ok(p) => (),
            Err(e) => fail!("Failed to unzip file: {}", e),
        };

        let mut waypoints = Vec::<(f32, f32)>::new();
        let file_path = Path::new("/tmp/doc.kml");
        let xml_file = BufferedReader::new(File::open(&file_path));
        let mut coordinates_open_tag = false;
        // We should use a real XML parser here, but Google Earth saves the
        // <coordinates> tag on one line, then the coordinates on the next,
        // then the closing </coordinates> tag on the next, so we'll just rely
        // on that fact
        for line_option in xml_file.lines() {
            match line_option {
                Ok(line) => {
                    if line.as_slice().contains("<coordinates>") {
                        coordinates_open_tag = true;
                    } else if coordinates_open_tag {
                        let mut latitude = 0.0f32;
                        let mut longitude = 0.0f32;
                        for long_lat_alt in line.as_slice().words() {
                            let mut iterator = long_lat_alt.split(',');
                            let mut success = true;
                            match iterator.next() {
                                Some(longitude_str) => {
                                    // Rust 0.13 feature :(
                                    let parsed_longitude: Option<f32> = longitude_str.parse();
                                    match parsed_longitude {
                                        Some(longitude_) => longitude = longitude_,
                                        None => {
                                            println!("Unable to parse longitude: '{}'", longitude_str);
                                            success = false;
                                        },
                                    }
                                },
                                None => println!("No longitude"),
                            }

                            match iterator.next() {
                                Some(latitude_str) => {
                                    // Rust 0.13 feature :(
                                    let parsed_latitude: Option<f32> = latitude_str.parse();
                                    match parsed_latitude {
                                        Some(latitude_) => latitude = latitude_,
                                        None => {
                                            println!("Unable to parse latitude: '{}'", latitude_str);
                                            success = false;
                                        },
                                    }
                                }
                            }

                            if (success) {
                                waypoints.push((latitude, longitude));
                            }
                        }
                        break;
                    }
                }
                Err(e) => println!("error {}", e),
            }
        }
        waypoints
    }
}

impl WaypointGenerator for KmlWaypointGenerator {
    fn get_current_waypoint(&self, x_m: f32, y_m: f32) -> (f32, f32) {
        // TODO
        (0.0, 0.0)
    }

    fn get_current_raw_waypoint(&self, x_m: f32, y_m: f32) -> (f32, f32) {
        // TODO
        (0.0, 0.0)
    }

    fn next(&self) {
        // TODO
    }

    fn reached(&self, x_m: f32, y_m: f32) -> bool {
        // TODO
        return false;
    }

    fn done(&self) -> bool {
        // TODO
        return false;
    }
}
<commit_msg>Write a custom float parser<commit_after>use std::io::BufferedReader;
use std::io::File;
use std::io::fs::PathExtensions;
use std::io::process::Command;

use waypoint_generator::WaypointGenerator;

/**
 * Loads and returns waypoints from a KML file.
 */
struct KmlWaypointGenerator {
    waypoints: Vec<(f32, f32)>,
    current_waypoint: u32,
}

impl KmlWaypointGenerator {
    pub fn new(file_name: &str) -> KmlWaypointGenerator {
        KmlWaypointGenerator {
            waypoints: KmlWaypointGenerator::load_waypoints(file_name),
            current_waypoint: 0
        }
    }

    /**
     * Parse a float from a string. The Pi has an older version of rustc, and
     * the official method for parsing a float differ between that version and
     * nightly, so we have to do ths manually.
     */
    fn parse_float(float_str: &str) -> Option<f32> {
        let mut value: f32 = 0.0;
        let mut negative = false;
        let mut negative_allowed = true;
        let mut decimal = false;
        let mut decimal_value: f32 = 0.0;
        let mut multiplier: f32 = 1.0;
        for letter in float_str.chars() {
            if letter == '-' {
                if negative_allowed {
                    negative = !negative;
                    continue;
                } else {
                    return None;
                }
            }
            negative_allowed = false;

            if letter == '.' {
                if decimal {
                    return None;
                }
                decimal = true;
                multiplier = 1.0;
                continue;
            }

            let digit_value = (letter as i32 - '0' as i32);
            if digit_value > 10 || digit_value < 0 {
                return None;
            }
            if decimal {
                decimal_value = decimal_value * 10.0 + digit_value as f32;
                multiplier *= 0.1;
            } else {
                value = value * 10.0 + digit_value as f32;
            }
        }

        Some(value + decimal_value * multiplier)
    }

    fn load_waypoints(file_name: &str) -> Vec<(f32, f32)> {
        let path = Path::new(file_name);
        if !path.exists() || !path.is_file() {
            fail!("File does not exist: {}", file_name);
        }

        // A KML file is a zip archive containing a single file named "doc.kml"
        // that is an XML file
        let temp_directory = "/tmp/waypoints";
        let zip_io_result = Command::new("unzip")
            .arg(file_name)
            .arg("-d")  // Output directory
            .arg(temp_directory)
            .spawn();
        match zip_io_result {
            Ok(p) => (),
            Err(e) => fail!("Failed to unzip file: {}", e),
        };

        let mut waypoints = Vec::<(f32, f32)>::new();
        let file_path = Path::new("/tmp/doc.kml");
        let mut xml_file = BufferedReader::new(File::open(&file_path));
        let mut coordinates_open_tag = false;
        // We should use a real XML parser here, but Google Earth saves the
        // <coordinates> tag on one line, then the coordinates on the next,
        // then the closing </coordinates> tag on the next, so we'll just rely
        // on that fact
        for line_option in xml_file.lines() {
            match line_option {
                Ok(line) => {
                    if line.as_slice().contains("<coordinates>") {
                        coordinates_open_tag = true;
                    } else if coordinates_open_tag {
                        let mut latitude = 0.0f32;
                        let mut longitude = 0.0f32;
                        for long_lat_alt in line.as_slice().words() {
                            let mut iterator = long_lat_alt.split(',');
                            let mut success = true;
                            match iterator.next() {
                                Some(longitude_str) => {
                                    let parsed_longitude: Option<f32> = KmlWaypointGenerator::parse_float(longitude_str);
                                    match parsed_longitude {
                                        Some(longitude_) => longitude = longitude_,
                                        None => {
                                            println!("Unable to parse longitude: '{}'", longitude_str);
                                            success = false;
                                        },
                                    }
                                },
                                None => println!("No longitude"),
                            }

                            match iterator.next() {
                                Some(latitude_str) => {
                                    // Rust 0.13 feature :(
                                    let parsed_latitude: Option<f32> = KmlWaypointGenerator::parse_float(latitude_str);
                                    match parsed_latitude {
                                        Some(latitude_) => latitude = latitude_,
                                        None => {
                                            println!("Unable to parse latitude: '{}'", latitude_str);
                                            success = false;
                                        },
                                    }
                                }
                                None => println!("No latitude"),
                            }

                            if (success) {
                                waypoints.push((latitude, longitude));
                            }
                        }
                        break;
                    }
                }
                Err(e) => println!("error {}", e),
            }
        }
        waypoints
    }
}

impl WaypointGenerator for KmlWaypointGenerator {
    fn get_current_waypoint(&self, x_m: f32, y_m: f32) -> (f32, f32) {
        // TODO
        (0.0, 0.0)
    }

    fn get_current_raw_waypoint(&self, x_m: f32, y_m: f32) -> (f32, f32) {
        // TODO
        (0.0, 0.0)
    }

    fn next(&self) {
        // TODO
    }

    fn reached(&self, x_m: f32, y_m: f32) -> bool {
        // TODO
        return false;
    }

    fn done(&self) -> bool {
        // TODO
        return false;
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of `std::os` functionality for unix systems

use prelude::v1::*;

use error::{FromError, Error};
use ffi::{self, CString};
use fmt;
use old_io::{IoError, IoResult};
use libc::{self, c_int, c_char, c_void};
use os::TMPBUF_SZ;
use os;
use path::{BytesContainer};
use ptr;
use str;
use sys::fs::FileDesc;

const BUF_BYTES : uint = 2048u;

/// Returns the platform-specific value of errno
pub fn errno() -> int {
    #[cfg(any(target_os = "macos",
              target_os = "ios",
              target_os = "freebsd"))]
    fn errno_location() -> *const c_int {
        extern {
            fn __error() -> *const c_int;
        }
        unsafe {
            __error()
        }
    }

    #[cfg(target_os = "dragonfly")]
    fn errno_location() -> *const c_int {
        extern {
            fn __dfly_error() -> *const c_int;
        }
        unsafe {
            __dfly_error()
        }
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    fn errno_location() -> *const c_int {
        extern {
            fn __errno_location() -> *const c_int;
        }
        unsafe {
            __errno_location()
        }
    }

    unsafe {
        (*errno_location()) as int
    }
}

/// Get a detailed string description for the given error number
pub fn error_string(errno: i32) -> String {
    #[cfg(any(target_os = "macos",
              target_os = "ios",
              target_os = "android",
              target_os = "freebsd",
              target_os = "dragonfly"))]
    fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
                  -> c_int {
        extern {
            fn strerror_r(errnum: c_int, buf: *mut c_char,
                          buflen: libc::size_t) -> c_int;
        }
        unsafe {
            strerror_r(errnum, buf, buflen)
        }
    }

    // GNU libc provides a non-compliant version of strerror_r by default
    // and requires macros to instead use the POSIX compliant variant.
    // So we just use __xpg_strerror_r which is always POSIX compliant
    #[cfg(target_os = "linux")]
    fn strerror_r(errnum: c_int, buf: *mut c_char,
                  buflen: libc::size_t) -> c_int {
        extern {
            fn __xpg_strerror_r(errnum: c_int,
                                buf: *mut c_char,
                                buflen: libc::size_t)
                                -> c_int;
        }
        unsafe {
            __xpg_strerror_r(errnum, buf, buflen)
        }
    }

    let mut buf = [0 as c_char; TMPBUF_SZ];

    let p = buf.as_mut_ptr();
    unsafe {
        if strerror_r(errno as c_int, p, buf.len() as libc::size_t) < 0 {
            panic!("strerror_r failure");
        }

        let p = p as *const _;
        str::from_utf8(ffi::c_str_to_bytes(&p)).unwrap().to_string()
    }
}

pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
    let mut fds = [0; 2];
    if libc::pipe(fds.as_mut_ptr()) == 0 {
        Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
    } else {
        Err(super::last_error())
    }
}

pub fn getcwd() -> IoResult<Path> {
    let mut buf = [0 as c_char; BUF_BYTES];
    unsafe {
        if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() {
            Err(IoError::last_error())
        } else {
            Ok(Path::new(ffi::c_str_to_bytes(&buf.as_ptr())))
        }
    }
}

pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
    extern {
        fn rust_env_pairs() -> *const *const c_char;
    }
    let mut environ = rust_env_pairs();
    if environ as uint == 0 {
        panic!("os::env() failure getting env string from OS: {}",
               os::last_os_error());
    }
    let mut result = Vec::new();
    while *environ != ptr::null() {
        let env_pair = ffi::c_str_to_bytes(&*environ).to_vec();
        result.push(env_pair);
        environ = environ.offset(1);
    }
    result
}

pub fn split_paths(unparsed: &[u8]) -> Vec<Path> {
    unparsed.split(|b| *b == b':').map(Path::new).collect()
}

pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
    let mut joined = Vec::new();
    let sep = b':';

    for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
        if i > 0 { joined.push(sep) }
        if path.contains(&sep) { return Err("path segment contains separator `:`") }
        joined.push_all(path);
    }

    Ok(joined)
}

#[cfg(target_os = "freebsd")]
pub fn load_self() -> Option<Vec<u8>> {
    unsafe {
        use libc::funcs::bsd44::*;
        use libc::consts::os::extra::*;
        let mut mib = vec![CTL_KERN as c_int,
                           KERN_PROC as c_int,
                           KERN_PROC_PATHNAME as c_int,
                           -1 as c_int];
        let mut sz: libc::size_t = 0;
        let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
                         ptr::null_mut(), &mut sz, ptr::null_mut(),
                         0u as libc::size_t);
        if err != 0 { return None; }
        if sz == 0 { return None; }
        let mut v: Vec<u8> = Vec::with_capacity(sz as uint);
        let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
                         v.as_mut_ptr() as *mut libc::c_void, &mut sz,
                         ptr::null_mut(), 0u as libc::size_t);
        if err != 0 { return None; }
        if sz == 0 { return None; }
        v.set_len(sz as uint - 1); // chop off trailing NUL
        Some(v)
    }
}

#[cfg(target_os = "dragonfly")]
pub fn load_self() -> Option<Vec<u8>> {
    use std::io;

    match old_io::fs::readlink(&Path::new("/proc/curproc/file")) {
        Ok(path) => Some(path.into_vec()),
        Err(..) => None
    }
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn load_self() -> Option<Vec<u8>> {
    use old_io;

    match old_io::fs::readlink(&Path::new("/proc/self/exe")) {
        Ok(path) => Some(path.into_vec()),
        Err(..) => None
    }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn load_self() -> Option<Vec<u8>> {
    unsafe {
        use libc::funcs::extra::_NSGetExecutablePath;
        let mut sz: u32 = 0;
        _NSGetExecutablePath(ptr::null_mut(), &mut sz);
        if sz == 0 { return None; }
        let mut v: Vec<u8> = Vec::with_capacity(sz as uint);
        let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
        if err != 0 { return None; }
        v.set_len(sz as uint - 1); // chop off trailing NUL
        Some(v)
    }
}

pub fn chdir(p: &Path) -> IoResult<()> {
    let p = CString::from_slice(p.as_vec());
    unsafe {
        match libc::chdir(p.as_ptr()) == (0 as c_int) {
            true => Ok(()),
            false => Err(IoError::last_error()),
        }
    }
}

pub fn page_size() -> uint {
    unsafe {
        libc::sysconf(libc::_SC_PAGESIZE) as uint
    }
}
<commit_msg>Fix wrong use std::io -> old_io<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of `std::os` functionality for unix systems

use prelude::v1::*;

use error::{FromError, Error};
use ffi::{self, CString};
use fmt;
use old_io::{IoError, IoResult};
use libc::{self, c_int, c_char, c_void};
use os::TMPBUF_SZ;
use os;
use path::{BytesContainer};
use ptr;
use str;
use sys::fs::FileDesc;

const BUF_BYTES : uint = 2048u;

/// Returns the platform-specific value of errno
pub fn errno() -> int {
    #[cfg(any(target_os = "macos",
              target_os = "ios",
              target_os = "freebsd"))]
    fn errno_location() -> *const c_int {
        extern {
            fn __error() -> *const c_int;
        }
        unsafe {
            __error()
        }
    }

    #[cfg(target_os = "dragonfly")]
    fn errno_location() -> *const c_int {
        extern {
            fn __dfly_error() -> *const c_int;
        }
        unsafe {
            __dfly_error()
        }
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    fn errno_location() -> *const c_int {
        extern {
            fn __errno_location() -> *const c_int;
        }
        unsafe {
            __errno_location()
        }
    }

    unsafe {
        (*errno_location()) as int
    }
}

/// Get a detailed string description for the given error number
pub fn error_string(errno: i32) -> String {
    #[cfg(any(target_os = "macos",
              target_os = "ios",
              target_os = "android",
              target_os = "freebsd",
              target_os = "dragonfly"))]
    fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
                  -> c_int {
        extern {
            fn strerror_r(errnum: c_int, buf: *mut c_char,
                          buflen: libc::size_t) -> c_int;
        }
        unsafe {
            strerror_r(errnum, buf, buflen)
        }
    }

    // GNU libc provides a non-compliant version of strerror_r by default
    // and requires macros to instead use the POSIX compliant variant.
    // So we just use __xpg_strerror_r which is always POSIX compliant
    #[cfg(target_os = "linux")]
    fn strerror_r(errnum: c_int, buf: *mut c_char,
                  buflen: libc::size_t) -> c_int {
        extern {
            fn __xpg_strerror_r(errnum: c_int,
                                buf: *mut c_char,
                                buflen: libc::size_t)
                                -> c_int;
        }
        unsafe {
            __xpg_strerror_r(errnum, buf, buflen)
        }
    }

    let mut buf = [0 as c_char; TMPBUF_SZ];

    let p = buf.as_mut_ptr();
    unsafe {
        if strerror_r(errno as c_int, p, buf.len() as libc::size_t) < 0 {
            panic!("strerror_r failure");
        }

        let p = p as *const _;
        str::from_utf8(ffi::c_str_to_bytes(&p)).unwrap().to_string()
    }
}

pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
    let mut fds = [0; 2];
    if libc::pipe(fds.as_mut_ptr()) == 0 {
        Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
    } else {
        Err(super::last_error())
    }
}

pub fn getcwd() -> IoResult<Path> {
    let mut buf = [0 as c_char; BUF_BYTES];
    unsafe {
        if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() {
            Err(IoError::last_error())
        } else {
            Ok(Path::new(ffi::c_str_to_bytes(&buf.as_ptr())))
        }
    }
}

pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> {
    extern {
        fn rust_env_pairs() -> *const *const c_char;
    }
    let mut environ = rust_env_pairs();
    if environ as uint == 0 {
        panic!("os::env() failure getting env string from OS: {}",
               os::last_os_error());
    }
    let mut result = Vec::new();
    while *environ != ptr::null() {
        let env_pair = ffi::c_str_to_bytes(&*environ).to_vec();
        result.push(env_pair);
        environ = environ.offset(1);
    }
    result
}

pub fn split_paths(unparsed: &[u8]) -> Vec<Path> {
    unparsed.split(|b| *b == b':').map(Path::new).collect()
}

pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> {
    let mut joined = Vec::new();
    let sep = b':';

    for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() {
        if i > 0 { joined.push(sep) }
        if path.contains(&sep) { return Err("path segment contains separator `:`") }
        joined.push_all(path);
    }

    Ok(joined)
}

#[cfg(target_os = "freebsd")]
pub fn load_self() -> Option<Vec<u8>> {
    unsafe {
        use libc::funcs::bsd44::*;
        use libc::consts::os::extra::*;
        let mut mib = vec![CTL_KERN as c_int,
                           KERN_PROC as c_int,
                           KERN_PROC_PATHNAME as c_int,
                           -1 as c_int];
        let mut sz: libc::size_t = 0;
        let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
                         ptr::null_mut(), &mut sz, ptr::null_mut(),
                         0u as libc::size_t);
        if err != 0 { return None; }
        if sz == 0 { return None; }
        let mut v: Vec<u8> = Vec::with_capacity(sz as uint);
        let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint,
                         v.as_mut_ptr() as *mut libc::c_void, &mut sz,
                         ptr::null_mut(), 0u as libc::size_t);
        if err != 0 { return None; }
        if sz == 0 { return None; }
        v.set_len(sz as uint - 1); // chop off trailing NUL
        Some(v)
    }
}

#[cfg(target_os = "dragonfly")]
pub fn load_self() -> Option<Vec<u8>> {
    use old_io;

    match old_io::fs::readlink(&Path::new("/proc/curproc/file")) {
        Ok(path) => Some(path.into_vec()),
        Err(..) => None
    }
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn load_self() -> Option<Vec<u8>> {
    use old_io;

    match old_io::fs::readlink(&Path::new("/proc/self/exe")) {
        Ok(path) => Some(path.into_vec()),
        Err(..) => None
    }
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
pub fn load_self() -> Option<Vec<u8>> {
    unsafe {
        use libc::funcs::extra::_NSGetExecutablePath;
        let mut sz: u32 = 0;
        _NSGetExecutablePath(ptr::null_mut(), &mut sz);
        if sz == 0 { return None; }
        let mut v: Vec<u8> = Vec::with_capacity(sz as uint);
        let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz);
        if err != 0 { return None; }
        v.set_len(sz as uint - 1); // chop off trailing NUL
        Some(v)
    }
}

pub fn chdir(p: &Path) -> IoResult<()> {
    let p = CString::from_slice(p.as_vec());
    unsafe {
        match libc::chdir(p.as_ptr()) == (0 as c_int) {
            true => Ok(()),
            false => Err(IoError::last_error()),
        }
    }
}

pub fn page_size() -> uint {
    unsafe {
        libc::sysconf(libc::_SC_PAGESIZE) as uint
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ast::{Data, Container, Style};
use attr::{Identifier, EnumTag};
use Ctxt;

/// Cross-cutting checks that require looking at more than a single attrs
/// object. Simpler checks should happen when parsing and building the attrs.
pub fn check(cx: &Ctxt, cont: &Container) {
    check_getter(cx, cont);
    check_identifier(cx, cont);
    check_variant_skip_attrs(cx, cont);
    check_internal_tag_field_name_conflict(cx, cont);
    check_adjacent_tag_conflict(cx, cont);
}

/// Getters are only allowed inside structs (not enums) with the `remote`
/// attribute.
fn check_getter(cx: &Ctxt, cont: &Container) {
    match cont.data {
        Data::Enum(_) => {
            if cont.data.has_getter() {
                cx.error("#[serde(getter = \"...\")] is not allowed in an enum");
            }
        }
        Data::Struct(_, _) => {
            if cont.data.has_getter() && cont.attrs.remote().is_none() {
                cx.error(
                    "#[serde(getter = \"...\")] can only be used in structs \
                     that have #[serde(remote = \"...\")]",
                );
            }
        }
    }
}

/// The `other` attribute must be used at most once and it must be the last
/// variant of an enum that has the `field_identifier` attribute.
///
/// Inside a `variant_identifier` all variants must be unit variants. Inside a
/// `field_identifier` all but possibly one variant must be unit variants. The
/// last variant may be a newtype variant which is an implicit "other" case.
fn check_identifier(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for (i, variant) in variants.iter().enumerate() {
        match (
            variant.style,
            cont.attrs.identifier(),
            variant.attrs.other(),
        ) {
            // The `other` attribute may only be used in a field_identifier.
            (_, Identifier::Variant, true) | (_, Identifier::No, true) => {
                cx.error("#[serde(other)] may only be used inside a field_identifier");
            }

            // Variant with `other` attribute must be the last one.
            (Style::Unit, Identifier::Field, true) => {
                if i < variants.len() - 1 {
                    cx.error("#[serde(other)] must be the last variant");
                }
            }

            // Variant with `other` attribute must be a unit variant.
            (_, Identifier::Field, true) => {
                cx.error("#[serde(other)] must be on a unit variant");
            }

            // Any sort of variant is allowed if this is not an identifier.
            (_, Identifier::No, false) => {}

            // Unit variant without `other` attribute is always fine.
            (Style::Unit, _, false) => {}

            // The last field is allowed to be a newtype catch-all.
            (Style::Newtype, Identifier::Field, false) => {
                if i < variants.len() - 1 {
                    cx.error(format!("`{}` must be the last variant", variant.ident));
                }
            }

            (_, Identifier::Field, false) => {
                cx.error("field_identifier may only contain unit variants");
            }

            (_, Identifier::Variant, false) => {
                cx.error("variant_identifier may only contain unit variants");
            }
        }
    }
}

/// Skip-(de)serializing attributes are not allowed on variants marked
/// (de)serialize_with.
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for variant in variants.iter() {
        if variant.attrs.serialize_with().is_some() {
            if variant.attrs.skip_serializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(serialize_with)] and \
                     #[serde(skip_serializing)]",
                    variant.ident
                ));
            }

            for (i, field) in variant.fields.iter().enumerate() {
                let ident = field
                    .ident
                    .as_ref()
                    .map_or_else(|| format!("{}", i), |ident| format!("`{}`", ident));

                if field.attrs.skip_serializing() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing)]",
                        variant.ident, ident
                    ));
                }

                if field.attrs.skip_serializing_if().is_some() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing_if)]",
                        variant.ident, ident
                    ));
                }
            }
        }

        if variant.attrs.deserialize_with().is_some() {
            if variant.attrs.skip_deserializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(deserialize_with)] and \
                     #[serde(skip_deserializing)]",
                    variant.ident
                ));
            }

            for (i, field) in variant.fields.iter().enumerate() {
                if field.attrs.skip_deserializing() {
                    let ident = field
                        .ident
                        .as_ref()
                        .map_or_else(|| format!("{}", i), |ident| format!("`{}`", ident));

                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(deserialize_with)] \
                         and a field {} marked with #[serde(skip_deserializing)]",
                        variant.ident, ident
                    ));
                }
            }
        }
    }
}

/// The tag of an internally-tagged struct variant must not be
/// the same as either one of its fields, as this would result in
/// duplicate keys in the serialized output and/or ambiguity in
/// the to-be-deserialized input.
fn check_internal_tag_field_name_conflict(
    cx: &Ctxt,
    cont: &Container,
) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => return,
    };

    let tag = match *cont.attrs.tag() {
        EnumTag::Internal { ref tag } => tag.as_str(),
        EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return,
    };

    let diagnose_conflict = || {
        let message = format!(
            "variant field name `{}` conflicts with internal tag",
            tag
        );
        cx.error(message);
    };

    for variant in variants {
        match variant.style {
            Style::Struct => {
                for field in &variant.fields {
                    let check_ser = !field.attrs.skip_serializing();
                    let check_de = !field.attrs.skip_deserializing();
                    let name = field.attrs.name();
                    let ser_name = name.serialize_name();
                    let de_name = name.deserialize_name();

                    if check_ser && ser_name == tag || check_de && de_name == tag {
                        diagnose_conflict();
                        return;
                    }
                }
            },
            Style::Unit | Style::Newtype | Style::Tuple => {},
        }
    }
}

/// In the case of adjacently-tagged enums, the type and the
/// contents tag must differ, for the same reason.
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
    let (type_tag, content_tag) = match *cont.attrs.tag() {
        EnumTag::Adjacent { ref tag, ref content } => (tag, content),
        EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return,
    };

    if type_tag == content_tag {
        let message = format!(
            "enum tags `{}` for type and content conflict with each other",
            type_tag
        );
        cx.error(message);
    }
}
<commit_msg>Added checks for flatten attribute<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use ast::{Data, Container, Style};
use attr::{Identifier, EnumTag};
use Ctxt;

/// Cross-cutting checks that require looking at more than a single attrs
/// object. Simpler checks should happen when parsing and building the attrs.
pub fn check(cx: &Ctxt, cont: &Container) {
    check_getter(cx, cont);
    check_flatten(cx, cont);
    check_identifier(cx, cont);
    check_variant_skip_attrs(cx, cont);
    check_internal_tag_field_name_conflict(cx, cont);
    check_adjacent_tag_conflict(cx, cont);
}

/// Getters are only allowed inside structs (not enums) with the `remote`
/// attribute.
fn check_getter(cx: &Ctxt, cont: &Container) {
    match cont.data {
        Data::Enum(_) => {
            if cont.data.has_getter() {
                cx.error("#[serde(getter = \"...\")] is not allowed in an enum");
            }
        }
        Data::Struct(_, _) => {
            if cont.data.has_getter() && cont.attrs.remote().is_none() {
                cx.error(
                    "#[serde(getter = \"...\")] can only be used in structs \
                     that have #[serde(remote = \"...\")]",
                );
            }
        }
    }
}

/// Flattening has some restrictions we can test.
fn check_flatten(cx: &Ctxt, cont: &Container) {
    match cont.data {
        Data::Enum(_) => {
            if cont.attrs.has_flatten() {
                cx.error("#[serde(flatten)] is not allowed in an enum");
            }
        }
        Data::Struct(_, _) => {
            for field in cont.data.all_fields() {
                if !field.attrs.flatten() {
                    continue;
                }
                if field.attrs.skip_serializing() {
                    cx.error(
                        "#[serde(flatten] can not be combined with \
                         #[serde(skip_serializing)]"
                    );
                } else if field.attrs.skip_serializing_if().is_some() {
                    cx.error(
                        "#[serde(flatten] can not be combined with \
                         #[serde(skip_serializing_if = \"...\")]"
                    );
                } else if field.attrs.skip_deserializing() {
                    cx.error(
                        "#[serde(flatten] can not be combined with \
                         #[serde(skip_deserializing)]"
                    );
                }
            }
        }
    }
}

/// The `other` attribute must be used at most once and it must be the last
/// variant of an enum that has the `field_identifier` attribute.
///
/// Inside a `variant_identifier` all variants must be unit variants. Inside a
/// `field_identifier` all but possibly one variant must be unit variants. The
/// last variant may be a newtype variant which is an implicit "other" case.
fn check_identifier(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for (i, variant) in variants.iter().enumerate() {
        match (
            variant.style,
            cont.attrs.identifier(),
            variant.attrs.other(),
        ) {
            // The `other` attribute may only be used in a field_identifier.
            (_, Identifier::Variant, true) | (_, Identifier::No, true) => {
                cx.error("#[serde(other)] may only be used inside a field_identifier");
            }

            // Variant with `other` attribute must be the last one.
            (Style::Unit, Identifier::Field, true) => {
                if i < variants.len() - 1 {
                    cx.error("#[serde(other)] must be the last variant");
                }
            }

            // Variant with `other` attribute must be a unit variant.
            (_, Identifier::Field, true) => {
                cx.error("#[serde(other)] must be on a unit variant");
            }

            // Any sort of variant is allowed if this is not an identifier.
            (_, Identifier::No, false) => {}

            // Unit variant without `other` attribute is always fine.
            (Style::Unit, _, false) => {}

            // The last field is allowed to be a newtype catch-all.
            (Style::Newtype, Identifier::Field, false) => {
                if i < variants.len() - 1 {
                    cx.error(format!("`{}` must be the last variant", variant.ident));
                }
            }

            (_, Identifier::Field, false) => {
                cx.error("field_identifier may only contain unit variants");
            }

            (_, Identifier::Variant, false) => {
                cx.error("variant_identifier may only contain unit variants");
            }
        }
    }
}

/// Skip-(de)serializing attributes are not allowed on variants marked
/// (de)serialize_with.
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for variant in variants.iter() {
        if variant.attrs.serialize_with().is_some() {
            if variant.attrs.skip_serializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(serialize_with)] and \
                     #[serde(skip_serializing)]",
                    variant.ident
                ));
            }

            for (i, field) in variant.fields.iter().enumerate() {
                let ident = field
                    .ident
                    .as_ref()
                    .map_or_else(|| format!("{}", i), |ident| format!("`{}`", ident));

                if field.attrs.skip_serializing() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing)]",
                        variant.ident, ident
                    ));
                }

                if field.attrs.skip_serializing_if().is_some() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing_if)]",
                        variant.ident, ident
                    ));
                }
            }
        }

        if variant.attrs.deserialize_with().is_some() {
            if variant.attrs.skip_deserializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(deserialize_with)] and \
                     #[serde(skip_deserializing)]",
                    variant.ident
                ));
            }

            for (i, field) in variant.fields.iter().enumerate() {
                if field.attrs.skip_deserializing() {
                    let ident = field
                        .ident
                        .as_ref()
                        .map_or_else(|| format!("{}", i), |ident| format!("`{}`", ident));

                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(deserialize_with)] \
                         and a field {} marked with #[serde(skip_deserializing)]",
                        variant.ident, ident
                    ));
                }
            }
        }
    }
}

/// The tag of an internally-tagged struct variant must not be
/// the same as either one of its fields, as this would result in
/// duplicate keys in the serialized output and/or ambiguity in
/// the to-be-deserialized input.
fn check_internal_tag_field_name_conflict(
    cx: &Ctxt,
    cont: &Container,
) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => return,
    };

    let tag = match *cont.attrs.tag() {
        EnumTag::Internal { ref tag } => tag.as_str(),
        EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return,
    };

    let diagnose_conflict = || {
        let message = format!(
            "variant field name `{}` conflicts with internal tag",
            tag
        );
        cx.error(message);
    };

    for variant in variants {
        match variant.style {
            Style::Struct => {
                for field in &variant.fields {
                    let check_ser = !field.attrs.skip_serializing();
                    let check_de = !field.attrs.skip_deserializing();
                    let name = field.attrs.name();
                    let ser_name = name.serialize_name();
                    let de_name = name.deserialize_name();

                    if check_ser && ser_name == tag || check_de && de_name == tag {
                        diagnose_conflict();
                        return;
                    }
                }
            },
            Style::Unit | Style::Newtype | Style::Tuple => {},
        }
    }
}

/// In the case of adjacently-tagged enums, the type and the
/// contents tag must differ, for the same reason.
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
    let (type_tag, content_tag) = match *cont.attrs.tag() {
        EnumTag::Adjacent { ref tag, ref content } => (tag, content),
        EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return,
    };

    if type_tag == content_tag {
        let message = format!(
            "enum tags `{}` for type and content conflict with each other",
            type_tag
        );
        cx.error(message);
    }
}
<|endoftext|>


<commit_before>use super::on_conflict_actions::*;
use super::on_conflict_clause::*;
use super::on_conflict_target::*;
use crate::query_builder::{AsChangeset, InsertStatement, UndecoratedInsertRecord};
use crate::query_source::QuerySource;

impl<T, U, Op, Ret> InsertStatement<T, U, Op, Ret>
where
    U: UndecoratedInsertRecord<T>,
{
    /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without
    /// specifying any columns or constraints to restrict the conflict to.
    ///
    /// # Examples
    ///
    /// ### Single Record
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// let user = User { id: 1, name: "Sean", };
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&user)
    ///     .on_conflict_do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), inserted_row_count);
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&user)
    ///     .on_conflict_do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(0), inserted_row_count);
    /// # }
    /// ```
    ///
    /// ### Vec of Records
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #[cfg(feature = "postgres")]
    /// let user = User { id: 1, name: "Sean", };
    ///
    /// #[cfg(feature = "postgres")]
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&vec![user, user])
    ///     .on_conflict_do_nothing()
    ///     .execute(&conn);
    /// #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(1), inserted_row_count);
    /// # }
    /// ```
    pub fn on_conflict_do_nothing(
        self,
    ) -> InsertStatement<T, OnConflictValues<U, NoConflictTarget, DoNothing>, Op, Ret> {
        self.replace_values(OnConflictValues::do_nothing)
    }

    /// Adds an `ON CONFLICT` to the insert statement, if a conflict occurs
    /// for the given unique constraint.
    ///
    /// `Target` can be one of:
    ///
    /// - A column
    /// - A tuple of columns
    /// - [`on_constraint("constraint_name")`][`on_constraint`]
    ///
    /// # Examples
    ///
    /// ### Specifying a column as the target
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// conn.execute("CREATE UNIQUE INDEX users_name ON users (name)").unwrap();
    /// let user = User { id: 1, name: "Sean", };
    /// let same_name_different_id = User { id: 2, name: "Sean" };
    /// let same_id_different_name = User { id: 1, name: "Pascal" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&same_name_different_id)
    ///     .on_conflict(name)
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(0), inserted_row_count);
    ///
    /// let pk_conflict_result = diesel::insert_into(users)
    ///     .values(&same_id_different_name)
    ///     .on_conflict(name)
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert!(pk_conflict_result.is_err());
    /// # }
    /// ```
    ///
    /// ### Specifying multiple columns as the target
    ///
    /// ```rust
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #         hair_color -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Clone, Copy, Insertable)]
    /// # #[table_name="users"]
    /// # struct User<'a> {
    /// #     id: i32,
    /// #     name: &'a str,
    /// #     hair_color: &'a str,
    /// # }
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// use diesel::upsert::*;
    ///
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE users").unwrap();
    /// #     conn.execute("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, hair_color TEXT)").unwrap();
    /// conn.execute("CREATE UNIQUE INDEX users_name_hair_color ON users (name, hair_color)").unwrap();
    /// let user = User { id: 1, name: "Sean", hair_color: "black" };
    /// let same_name_different_hair_color = User { id: 2, name: "Sean", hair_color: "brown" };
    /// let same_name_same_hair_color = User { id: 3, name: "Sean", hair_color: "black" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&same_name_different_hair_color)
    ///     .on_conflict((name, hair_color))
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), inserted_row_count);
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&same_name_same_hair_color)
    ///     .on_conflict((name, hair_color))
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(0), inserted_row_count);
    /// # }
    /// ```
    ///
    /// See the documentation for [`on_constraint`] and [`do_update`] for
    /// more examples.
    ///
    /// [`on_constraint`]: ../upsert/fn.on_constraint.html
    /// [`do_update`]: ../upsert/struct.IncompleteOnConflict.html#method.do_update
    pub fn on_conflict<Target>(
        self,
        target: Target,
    ) -> IncompleteOnConflict<Self, ConflictTarget<Target>>
    where
        ConflictTarget<Target>: OnConflictTarget<T>,
    {
        IncompleteOnConflict {
            stmt: self,
            target: ConflictTarget(target),
        }
    }
}

/// A partially constructed `ON CONFLICT` clause.
#[derive(Debug, Clone, Copy)]
pub struct IncompleteOnConflict<Stmt, Target> {
    stmt: Stmt,
    target: Target,
}

impl<T, U, Op, Ret, Target> IncompleteOnConflict<InsertStatement<T, U, Op, Ret>, Target> {
    /// Creates a query with `ON CONFLICT (target) DO NOTHING`
    ///
    /// If you want to do nothing when *any* constraint conflicts, use
    /// [`on_conflict_do_nothing`] instead. See [`on_conflict`] for usage
    /// examples.
    ///
    /// [`on_conflict_do_nothing`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict_do_nothing
    /// [`on_conflict`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict
    pub fn do_nothing(self) -> InsertStatement<T, OnConflictValues<U, Target, DoNothing>, Op, Ret> {
        let target = self.target;
        self.stmt
            .replace_values(|values| OnConflictValues::new(values, target, DoNothing))
    }
}

impl<Stmt, Target> IncompleteOnConflict<Stmt, Target> {
    /// Used to create a query in the form `ON CONFLICT (...) DO UPDATE ...`
    ///
    /// Call `.set` on the result of this function with the changes you want to
    /// apply. The argument to `set` can be anything that implements `AsChangeset`
    /// (e.g. anything you could pass to `set` on a normal update statement).
    ///
    /// Note: When inserting more than one row at a time, this query can still fail
    /// if the rows being inserted conflict with each other.
    ///
    /// # Examples
    ///
    /// ## Set specific value on conflict
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// let user = User { id: 1, name: "Pascal" };
    /// let user2 = User { id: 1, name: "Sean" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let insert_count = diesel::insert_into(users)
    ///     .values(&user2)
    ///     .on_conflict(id)
    ///     .do_update()
    ///     .set(name.eq("I DONT KNOW ANYMORE"))
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), insert_count);
    ///
    /// let users_in_db = users.load(&conn);
    /// assert_eq!(Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]), users_in_db);
    /// # }
    /// ```
    ///
    /// ## Set `AsChangeset` struct on conflict
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// let user = User { id: 1, name: "Pascal" };
    /// let user2 = User { id: 1, name: "Sean" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let insert_count = diesel::insert_into(users)
    ///     .values(&user2)
    ///     .on_conflict(id)
    ///     .do_update()
    ///     .set(&user2)
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), insert_count);
    ///
    /// let users_in_db = users.load(&conn);
    /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db);
    /// # }
    /// ```
    ///
    /// ## Use `excluded` to get the rejected value
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// use diesel::upsert::excluded;
    ///
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// let user = User { id: 1, name: "Pascal" };
    /// let user2 = User { id: 1, name: "Sean" };
    /// let user3 = User { id: 2, name: "Tess" };
    ///
    /// #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// #[cfg(feature = "postgres")]
    /// let insert_count = diesel::insert_into(users)
    ///     .values(&vec![user2, user3])
    ///     .on_conflict(id)
    ///     .do_update()
    ///     .set(name.eq(excluded(name)))
    ///     .execute(&conn);
    /// #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(2), insert_count);
    ///
    /// #[cfg(feature = "postgres")]
    /// let users_in_db = users.load(&conn);
    /// #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(vec![(1, "Sean".to_string()), (2, "Tess".to_string())]), users_in_db);
    /// # }
    /// ```
    pub fn do_update(self) -> IncompleteDoUpdate<Stmt, Target> {
        IncompleteDoUpdate {
            stmt: self.stmt,
            target: self.target,
        }
    }
}

/// A partially constructed `ON CONFLICT DO UPDATE` clause.
#[derive(Debug, Clone, Copy)]
pub struct IncompleteDoUpdate<Stmt, Target> {
    stmt: Stmt,
    target: Target,
}

impl<T, U, Op, Ret, Target> IncompleteDoUpdate<InsertStatement<T, U, Op, Ret>, Target> {
    /// See [`do_update`] for usage examples.
    ///
    /// [`do_update`]: struct.IncompleteOnConflict.html#method.do_update
    pub fn set<Changes>(
        self,
        changes: Changes,
    ) -> InsertStatement<T, OnConflictValues<U, Target, DoUpdate<Changes::Changeset>>, Op, Ret>
    where
        T: QuerySource,
        Changes: AsChangeset<Target = T>,
    {
        let target = self.target;
        self.stmt.replace_values(|values| {
            OnConflictValues::new(values, target, DoUpdate::new(changes.as_changeset()))
        })
    }
}
<commit_msg>Hide cfg attribute<commit_after>use super::on_conflict_actions::*;
use super::on_conflict_clause::*;
use super::on_conflict_target::*;
use crate::query_builder::{AsChangeset, InsertStatement, UndecoratedInsertRecord};
use crate::query_source::QuerySource;

impl<T, U, Op, Ret> InsertStatement<T, U, Op, Ret>
where
    U: UndecoratedInsertRecord<T>,
{
    /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without
    /// specifying any columns or constraints to restrict the conflict to.
    ///
    /// # Examples
    ///
    /// ### Single Record
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// let user = User { id: 1, name: "Sean", };
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&user)
    ///     .on_conflict_do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), inserted_row_count);
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&user)
    ///     .on_conflict_do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(0), inserted_row_count);
    /// # }
    /// ```
    ///
    /// ### Vec of Records
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// # #[cfg(feature = "postgres")]
    /// let user = User { id: 1, name: "Sean", };
    ///
    /// # #[cfg(feature = "postgres")]
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&vec![user, user])
    ///     .on_conflict_do_nothing()
    ///     .execute(&conn);
    /// # #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(1), inserted_row_count);
    /// # }
    /// ```
    pub fn on_conflict_do_nothing(
        self,
    ) -> InsertStatement<T, OnConflictValues<U, NoConflictTarget, DoNothing>, Op, Ret> {
        self.replace_values(OnConflictValues::do_nothing)
    }

    /// Adds an `ON CONFLICT` to the insert statement, if a conflict occurs
    /// for the given unique constraint.
    ///
    /// `Target` can be one of:
    ///
    /// - A column
    /// - A tuple of columns
    /// - [`on_constraint("constraint_name")`][`on_constraint`]
    ///
    /// # Examples
    ///
    /// ### Specifying a column as the target
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// conn.execute("CREATE UNIQUE INDEX users_name ON users (name)").unwrap();
    /// let user = User { id: 1, name: "Sean", };
    /// let same_name_different_id = User { id: 2, name: "Sean" };
    /// let same_id_different_name = User { id: 1, name: "Pascal" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&same_name_different_id)
    ///     .on_conflict(name)
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(0), inserted_row_count);
    ///
    /// let pk_conflict_result = diesel::insert_into(users)
    ///     .values(&same_id_different_name)
    ///     .on_conflict(name)
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert!(pk_conflict_result.is_err());
    /// # }
    /// ```
    ///
    /// ### Specifying multiple columns as the target
    ///
    /// ```rust
    /// # include!("../../doctest_setup.rs");
    /// #
    /// # table! {
    /// #     users {
    /// #         id -> Integer,
    /// #         name -> VarChar,
    /// #         hair_color -> VarChar,
    /// #     }
    /// # }
    /// #
    /// # #[derive(Clone, Copy, Insertable)]
    /// # #[table_name="users"]
    /// # struct User<'a> {
    /// #     id: i32,
    /// #     name: &'a str,
    /// #     hair_color: &'a str,
    /// # }
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// use diesel::upsert::*;
    ///
    /// #     let conn = establish_connection();
    /// #     conn.execute("DROP TABLE users").unwrap();
    /// #     conn.execute("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, hair_color TEXT)").unwrap();
    /// conn.execute("CREATE UNIQUE INDEX users_name_hair_color ON users (name, hair_color)").unwrap();
    /// let user = User { id: 1, name: "Sean", hair_color: "black" };
    /// let same_name_different_hair_color = User { id: 2, name: "Sean", hair_color: "brown" };
    /// let same_name_same_hair_color = User { id: 3, name: "Sean", hair_color: "black" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&same_name_different_hair_color)
    ///     .on_conflict((name, hair_color))
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), inserted_row_count);
    ///
    /// let inserted_row_count = diesel::insert_into(users)
    ///     .values(&same_name_same_hair_color)
    ///     .on_conflict((name, hair_color))
    ///     .do_nothing()
    ///     .execute(&conn);
    /// assert_eq!(Ok(0), inserted_row_count);
    /// # }
    /// ```
    ///
    /// See the documentation for [`on_constraint`] and [`do_update`] for
    /// more examples.
    ///
    /// [`on_constraint`]: ../upsert/fn.on_constraint.html
    /// [`do_update`]: ../upsert/struct.IncompleteOnConflict.html#method.do_update
    pub fn on_conflict<Target>(
        self,
        target: Target,
    ) -> IncompleteOnConflict<Self, ConflictTarget<Target>>
    where
        ConflictTarget<Target>: OnConflictTarget<T>,
    {
        IncompleteOnConflict {
            stmt: self,
            target: ConflictTarget(target),
        }
    }
}

/// A partially constructed `ON CONFLICT` clause.
#[derive(Debug, Clone, Copy)]
pub struct IncompleteOnConflict<Stmt, Target> {
    stmt: Stmt,
    target: Target,
}

impl<T, U, Op, Ret, Target> IncompleteOnConflict<InsertStatement<T, U, Op, Ret>, Target> {
    /// Creates a query with `ON CONFLICT (target) DO NOTHING`
    ///
    /// If you want to do nothing when *any* constraint conflicts, use
    /// [`on_conflict_do_nothing`] instead. See [`on_conflict`] for usage
    /// examples.
    ///
    /// [`on_conflict_do_nothing`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict_do_nothing
    /// [`on_conflict`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict
    pub fn do_nothing(self) -> InsertStatement<T, OnConflictValues<U, Target, DoNothing>, Op, Ret> {
        let target = self.target;
        self.stmt
            .replace_values(|values| OnConflictValues::new(values, target, DoNothing))
    }
}

impl<Stmt, Target> IncompleteOnConflict<Stmt, Target> {
    /// Used to create a query in the form `ON CONFLICT (...) DO UPDATE ...`
    ///
    /// Call `.set` on the result of this function with the changes you want to
    /// apply. The argument to `set` can be anything that implements `AsChangeset`
    /// (e.g. anything you could pass to `set` on a normal update statement).
    ///
    /// Note: When inserting more than one row at a time, this query can still fail
    /// if the rows being inserted conflict with each other.
    ///
    /// # Examples
    ///
    /// ## Set specific value on conflict
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// let user = User { id: 1, name: "Pascal" };
    /// let user2 = User { id: 1, name: "Sean" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let insert_count = diesel::insert_into(users)
    ///     .values(&user2)
    ///     .on_conflict(id)
    ///     .do_update()
    ///     .set(name.eq("I DONT KNOW ANYMORE"))
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), insert_count);
    ///
    /// let users_in_db = users.load(&conn);
    /// assert_eq!(Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]), users_in_db);
    /// # }
    /// ```
    ///
    /// ## Set `AsChangeset` struct on conflict
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// #     #[cfg(feature = "sqlite")]
    /// #     conn.execute("DELETE FROM users").unwrap();
    /// let user = User { id: 1, name: "Pascal" };
    /// let user2 = User { id: 1, name: "Sean" };
    ///
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// let insert_count = diesel::insert_into(users)
    ///     .values(&user2)
    ///     .on_conflict(id)
    ///     .do_update()
    ///     .set(&user2)
    ///     .execute(&conn);
    /// assert_eq!(Ok(1), insert_count);
    ///
    /// let users_in_db = users.load(&conn);
    /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db);
    /// # }
    /// ```
    ///
    /// ## Use `excluded` to get the rejected value
    ///
    /// ```rust
    /// # include!("on_conflict_docs_setup.rs");
    /// #
    /// # fn main() {
    /// #     use self::users::dsl::*;
    /// use diesel::upsert::excluded;
    ///
    /// #     let conn = establish_connection();
    /// #     #[cfg(feature = "postgres")]
    /// #     conn.execute("TRUNCATE TABLE users").unwrap();
    /// let user = User { id: 1, name: "Pascal" };
    /// let user2 = User { id: 1, name: "Sean" };
    /// let user3 = User { id: 2, name: "Tess" };
    ///
    /// # #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn));
    ///
    /// #[cfg(feature = "postgres")]
    /// let insert_count = diesel::insert_into(users)
    ///     .values(&vec![user2, user3])
    ///     .on_conflict(id)
    ///     .do_update()
    ///     .set(name.eq(excluded(name)))
    ///     .execute(&conn);
    /// # #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(2), insert_count);
    ///
    /// # #[cfg(feature = "postgres")]
    /// let users_in_db = users.load(&conn);
    /// # #[cfg(feature = "postgres")]
    /// assert_eq!(Ok(vec![(1, "Sean".to_string()), (2, "Tess".to_string())]), users_in_db);
    /// # }
    /// ```
    pub fn do_update(self) -> IncompleteDoUpdate<Stmt, Target> {
        IncompleteDoUpdate {
            stmt: self.stmt,
            target: self.target,
        }
    }
}

/// A partially constructed `ON CONFLICT DO UPDATE` clause.
#[derive(Debug, Clone, Copy)]
pub struct IncompleteDoUpdate<Stmt, Target> {
    stmt: Stmt,
    target: Target,
}

impl<T, U, Op, Ret, Target> IncompleteDoUpdate<InsertStatement<T, U, Op, Ret>, Target> {
    /// See [`do_update`] for usage examples.
    ///
    /// [`do_update`]: struct.IncompleteOnConflict.html#method.do_update
    pub fn set<Changes>(
        self,
        changes: Changes,
    ) -> InsertStatement<T, OnConflictValues<U, Target, DoUpdate<Changes::Changeset>>, Op, Ret>
    where
        T: QuerySource,
        Changes: AsChangeset<Target = T>,
    {
        let target = self.target;
        self.stmt.replace_values(|values| {
            OnConflictValues::new(values, target, DoUpdate::new(changes.as_changeset()))
        })
    }
}
<|endoftext|>


<commit_before><commit_msg>rename inventory to tracker<commit_after><|endoftext|>


<commit_before>use super::{Cell, Features, Mesh, Point};
use crate::mesh::Extract;
use crate::util::{GridSearch, GsNdiv, GsTol};
use crate::StrError;

/// Joins two meshes by comparing the coordinates on the boundary of the first mesh
///
/// **Note:** The meshes must have the same space_ndim.
///
/// **Important:** This function does not guarantee the "mesh compatibility" requirements
/// for finite element analyses.
pub fn join_meshes(a: &Mesh, b: &Mesh) -> Result<Mesh, StrError> {
    // check
    if a.ndim != b.ndim {
        return Err("meshes must have the same space_ndim");
    }

    // find the boundary of mesh A
    let (_, _, boundary_a) = Features::new(a, Extract::Boundary);

    // allocate and prepare a GridSearch for mesh A
    let mut grid_a = GridSearch::new(&boundary_a.min, &boundary_a.max, 0.01, GsNdiv::Default, GsTol::Default)?;
    for m in 0..a.points.len() {
        grid_a.insert(a.points[m].id, &a.points[m].coords)?;
    }

    // create a new mesh with all mesh A data
    let mut mesh = a.clone();

    // renumber the points of mesh B and add them to the new mesh (if not present yet)
    let mut new_point_id = a.points.len();
    let mut map_old_to_new_point_id_b = vec![0; b.points.len()];
    for m in 0..b.points.len() {
        let x = &b.points[m].coords;
        let maybe_point_id_a = if grid_a.is_outside(x) { None } else { grid_a.find(x)? };
        let id = match maybe_point_id_a {
            Some(point_id_a) => point_id_a,
            None => {
                mesh.points.push(Point {
                    id: new_point_id,
                    coords: x.clone(),
                });
                new_point_id += 1;
                new_point_id - 1
            }
        };
        map_old_to_new_point_id_b[m] = id;
    }

    // insert cells of mesh B into new mesh
    let mut new_cell_id = a.cells.len();
    for cell in &b.cells {
        mesh.cells.push(Cell {
            id: new_cell_id,
            attribute_id: cell.attribute_id,
            kind: cell.kind,
            points: cell.points.iter().map(|id| map_old_to_new_point_id_b[*id]).collect(),
        });
        new_cell_id += 1;
    }
    Ok(mesh)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::join_meshes;
    use crate::mesh::{check_ids_and_kind, check_jacobian, Samples};
    use crate::StrError;

    #[test]
    fn join_meshes_works_2d() -> Result<(), StrError> {
        //          [#] indicates id
        //      y   (#) indicates attribute_id
        //      ↑
        // 1.0  3-----------2-----------5
        //      |           |           |
        //      |    [0]    |    [1]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 0.0  0-----------1-----------4  → x
        //
        //                  +
        //
        // 1.0  3-----------2-----------5
        //      |           |           |
        //      |    [0]    |    [1]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 0.0  0-----------1-----------4  → x
        //     0.0         1.0         2.0
        let a = Samples::two_quads_horizontal();
        let mut b = Samples::two_quads_horizontal();

        // shift B-mesh up
        for m in 0..b.points.len() {
            b.points[m].coords[1] += 1.0;
        }

        //      y
        //      ↑
        // 2.0  7-----------6-----------8
        //      |           |           |
        //      |    [2]    |    [3]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 1.0  3-----------2-----------5
        //      |           |           |
        //      |    [0]    |    [1]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 0.0  0-----------1-----------4  → x
        //     0.0         1.0         2.0
        let mesh = join_meshes(&a, &b)?;
        check_ids_and_kind(&mesh)?;
        check_jacobian(&mesh)?;
        assert_eq!(mesh.points[0].coords, &[0.0, 0.0]);
        assert_eq!(mesh.points[1].coords, &[1.0, 0.0]);
        assert_eq!(mesh.points[2].coords, &[1.0, 1.0]);
        assert_eq!(mesh.points[3].coords, &[0.0, 1.0]);
        assert_eq!(mesh.points[4].coords, &[2.0, 0.0]);
        assert_eq!(mesh.points[5].coords, &[2.0, 1.0]);
        assert_eq!(mesh.points[6].coords, &[1.0, 2.0]);
        assert_eq!(mesh.points[7].coords, &[0.0, 2.0]);
        assert_eq!(mesh.points[8].coords, &[2.0, 2.0]);
        assert_eq!(mesh.cells[0].points, &[0, 1, 2, 3]);
        assert_eq!(mesh.cells[1].points, &[1, 4, 5, 2]);
        assert_eq!(mesh.cells[2].points, &[3, 2, 6, 7]);
        assert_eq!(mesh.cells[3].points, &[2, 5, 8, 6]);
        assert_eq!(mesh.cells[0].attribute_id, 1);
        assert_eq!(mesh.cells[1].attribute_id, 2);
        assert_eq!(mesh.cells[2].attribute_id, 1);
        assert_eq!(mesh.cells[3].attribute_id, 2);
        Ok(())
    }

    #[test]
    fn join_meshes_works_3d() -> Result<(), StrError> {
        //       8-------------11  2.0          8-------------11  2.0
        //      /.             /|              /.             /|
        //     / .            / |             / .            / |
        //    /  .           /  |            /  .           /  |
        //   /   .          /   |           /   .          /   |
        //  9-------------10    |          9-------------10    |
        //  |    .         |    |          |    .         |    |
        //  |    4---------|----7  1.0     |    4---------|----7  1.0
        //  |   /. [1]     |   /|          |   /. [1]     |   /|
        //  |  / . (2)     |  / |      +   |  / . (2)     |  / |
        //  | /  .         | /  |          | /  .         | /  |
        //  |/   .         |/   |          |/   .         |/   |
        //  5--------------6    |          5--------------6    |          z
        //  |    .         |    |          |    .         |    |          ↑
        //  |    0---------|----3  0.0     |    0---------|----3  0.0     o → y
        //  |   /  [0]     |   /           |   /  [0]     |   /          ↙
        //  |  /   (1)     |  /            |  /   (1)     |  /          x
        //  | /            | /             | /            | /
        //  |/             |/              |/             |/
        //  1--------------2   1.0         1--------------2   1.0
        let a = Samples::two_cubes_vertical();
        let mut b = Samples::two_cubes_vertical();

        // shift mesh B along y (to the right)
        for m in 0..b.points.len() {
            b.points[m].coords[1] += 1.0;
        }

        //       8-------------11-------------17  2.0
        //      /.             /.             /|
        //     / .            / .            / |
        //    /  .           /  .           /  |
        //   /   .          /   .          /   |
        //  9=============10=============16    |
        //  |    .         |    .         |    |
        //  |    4---------|----7---------|---15  1.0
        //  |   /. [1]     |   /. [3]     |   /|
        //  |  / . (2)     |  / . (2)     |  / |
        //  | /  .         | /  .         | /  |
        //  |/   .         |/   .         |/   |
        //  5--------------6-------------14    |          z
        //  |    .         |    .         |    |          ↑
        //  |    0---------|----3---------|---13  0.0     o → y
        //  |   /  [0]     |   /  [2]     |   /          ↙
        //  |  /   (1)     |  /   (1)     |  /          x
        //  | /            | /            | /
        //  |/             |/             |/
        //  1--------------2-------------12   1.0
        // 0.0            1.0            2.0
        let mesh = join_meshes(&a, &b)?;
        assert_eq!(mesh.ndim, 3);
        assert_eq!(mesh.points.len(), 18);
        assert_eq!(mesh.cells.len(), 4);
        check_ids_and_kind(&mesh)?;
        check_jacobian(&mesh)?;

        let sample = Samples::four_cubes_wall();
        for i in 0..mesh.cells.len() {
            assert_eq!(mesh.cells[i].id, sample.cells[i].id);
            assert_eq!(mesh.cells[i].points, sample.cells[i].points);
        }
        for i in 0..mesh.points.len() {
            assert_eq!(mesh.points[i].id, sample.points[i].id);
            assert_eq!(mesh.points[i].coords, sample.points[i].coords);
        }
        Ok(())
    }
}
<commit_msg>Add test<commit_after>use super::{Cell, Features, Mesh, Point};
use crate::mesh::Extract;
use crate::util::{GridSearch, GsNdiv, GsTol};
use crate::StrError;

/// Joins two meshes by comparing the coordinates on the boundary of the first mesh
///
/// **Note:** The meshes must have the same space_ndim.
///
/// **Important:** This function does not guarantee the "mesh compatibility" requirements
/// for finite element analyses.
pub fn join_meshes(a: &Mesh, b: &Mesh) -> Result<Mesh, StrError> {
    // check
    if a.ndim != b.ndim {
        return Err("meshes must have the same ndim");
    }

    // find the boundary of mesh A
    let (_, _, boundary_a) = Features::new(a, Extract::Boundary);

    // allocate and prepare a GridSearch for mesh A
    let mut grid_a = GridSearch::new(&boundary_a.min, &boundary_a.max, 0.01, GsNdiv::Default, GsTol::Default)?;
    for m in 0..a.points.len() {
        grid_a.insert(a.points[m].id, &a.points[m].coords)?;
    }

    // create a new mesh with all mesh A data
    let mut mesh = a.clone();

    // renumber the points of mesh B and add them to the new mesh (if not present yet)
    let mut new_point_id = a.points.len();
    let mut map_old_to_new_point_id_b = vec![0; b.points.len()];
    for m in 0..b.points.len() {
        let x = &b.points[m].coords;
        let maybe_point_id_a = if grid_a.is_outside(x) { None } else { grid_a.find(x)? };
        let id = match maybe_point_id_a {
            Some(point_id_a) => point_id_a,
            None => {
                mesh.points.push(Point {
                    id: new_point_id,
                    coords: x.clone(),
                });
                new_point_id += 1;
                new_point_id - 1
            }
        };
        map_old_to_new_point_id_b[m] = id;
    }

    // insert cells of mesh B into new mesh
    let mut new_cell_id = a.cells.len();
    for cell in &b.cells {
        mesh.cells.push(Cell {
            id: new_cell_id,
            attribute_id: cell.attribute_id,
            kind: cell.kind,
            points: cell.points.iter().map(|id| map_old_to_new_point_id_b[*id]).collect(),
        });
        new_cell_id += 1;
    }
    Ok(mesh)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::join_meshes;
    use crate::mesh::{check_ids_and_kind, check_jacobian, Samples};
    use crate::StrError;

    #[test]
    fn join_meshes_handles_errors() {
        let a = Samples::two_quads_horizontal();
        let b = Samples::two_cubes_vertical();
        assert_eq!(join_meshes(&a, &b).err(), Some("meshes must have the same ndim"));
    }

    #[test]
    fn join_meshes_works_2d() -> Result<(), StrError> {
        //          [#] indicates id
        //      y   (#) indicates attribute_id
        //      ↑
        // 1.0  3-----------2-----------5
        //      |           |           |
        //      |    [0]    |    [1]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 0.0  0-----------1-----------4  → x
        //
        //                  +
        //
        // 1.0  3-----------2-----------5
        //      |           |           |
        //      |    [0]    |    [1]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 0.0  0-----------1-----------4  → x
        //     0.0         1.0         2.0
        let a = Samples::two_quads_horizontal();
        let mut b = Samples::two_quads_horizontal();

        // shift B-mesh up
        for m in 0..b.points.len() {
            b.points[m].coords[1] += 1.0;
        }

        //      y
        //      ↑
        // 2.0  7-----------6-----------8
        //      |           |           |
        //      |    [2]    |    [3]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 1.0  3-----------2-----------5
        //      |           |           |
        //      |    [0]    |    [1]    |
        //      |    (1)    |    (2)    |
        //      |           |           |
        // 0.0  0-----------1-----------4  → x
        //     0.0         1.0         2.0
        let mesh = join_meshes(&a, &b)?;
        check_ids_and_kind(&mesh)?;
        check_jacobian(&mesh)?;
        assert_eq!(mesh.points[0].coords, &[0.0, 0.0]);
        assert_eq!(mesh.points[1].coords, &[1.0, 0.0]);
        assert_eq!(mesh.points[2].coords, &[1.0, 1.0]);
        assert_eq!(mesh.points[3].coords, &[0.0, 1.0]);
        assert_eq!(mesh.points[4].coords, &[2.0, 0.0]);
        assert_eq!(mesh.points[5].coords, &[2.0, 1.0]);
        assert_eq!(mesh.points[6].coords, &[1.0, 2.0]);
        assert_eq!(mesh.points[7].coords, &[0.0, 2.0]);
        assert_eq!(mesh.points[8].coords, &[2.0, 2.0]);
        assert_eq!(mesh.cells[0].points, &[0, 1, 2, 3]);
        assert_eq!(mesh.cells[1].points, &[1, 4, 5, 2]);
        assert_eq!(mesh.cells[2].points, &[3, 2, 6, 7]);
        assert_eq!(mesh.cells[3].points, &[2, 5, 8, 6]);
        assert_eq!(mesh.cells[0].attribute_id, 1);
        assert_eq!(mesh.cells[1].attribute_id, 2);
        assert_eq!(mesh.cells[2].attribute_id, 1);
        assert_eq!(mesh.cells[3].attribute_id, 2);
        Ok(())
    }

    #[test]
    fn join_meshes_works_3d() -> Result<(), StrError> {
        //       8-------------11  2.0          8-------------11  2.0
        //      /.             /|              /.             /|
        //     / .            / |             / .            / |
        //    /  .           /  |            /  .           /  |
        //   /   .          /   |           /   .          /   |
        //  9-------------10    |          9-------------10    |
        //  |    .         |    |          |    .         |    |
        //  |    4---------|----7  1.0     |    4---------|----7  1.0
        //  |   /. [1]     |   /|          |   /. [1]     |   /|
        //  |  / . (2)     |  / |      +   |  / . (2)     |  / |
        //  | /  .         | /  |          | /  .         | /  |
        //  |/   .         |/   |          |/   .         |/   |
        //  5--------------6    |          5--------------6    |          z
        //  |    .         |    |          |    .         |    |          ↑
        //  |    0---------|----3  0.0     |    0---------|----3  0.0     o → y
        //  |   /  [0]     |   /           |   /  [0]     |   /          ↙
        //  |  /   (1)     |  /            |  /   (1)     |  /          x
        //  | /            | /             | /            | /
        //  |/             |/              |/             |/
        //  1--------------2   1.0         1--------------2   1.0
        let a = Samples::two_cubes_vertical();
        let mut b = Samples::two_cubes_vertical();

        // shift mesh B along y (to the right)
        for m in 0..b.points.len() {
            b.points[m].coords[1] += 1.0;
        }

        //       8-------------11-------------17  2.0
        //      /.             /.             /|
        //     / .            / .            / |
        //    /  .           /  .           /  |
        //   /   .          /   .          /   |
        //  9=============10=============16    |
        //  |    .         |    .         |    |
        //  |    4---------|----7---------|---15  1.0
        //  |   /. [1]     |   /. [3]     |   /|
        //  |  / . (2)     |  / . (2)     |  / |
        //  | /  .         | /  .         | /  |
        //  |/   .         |/   .         |/   |
        //  5--------------6-------------14    |          z
        //  |    .         |    .         |    |          ↑
        //  |    0---------|----3---------|---13  0.0     o → y
        //  |   /  [0]     |   /  [2]     |   /          ↙
        //  |  /   (1)     |  /   (1)     |  /          x
        //  | /            | /            | /
        //  |/             |/             |/
        //  1--------------2-------------12   1.0
        // 0.0            1.0            2.0
        let mesh = join_meshes(&a, &b)?;
        assert_eq!(mesh.ndim, 3);
        assert_eq!(mesh.points.len(), 18);
        assert_eq!(mesh.cells.len(), 4);
        check_ids_and_kind(&mesh)?;
        check_jacobian(&mesh)?;

        let sample = Samples::four_cubes_wall();
        for i in 0..mesh.cells.len() {
            assert_eq!(mesh.cells[i].id, sample.cells[i].id);
            assert_eq!(mesh.cells[i].points, sample.cells[i].points);
        }
        for i in 0..mesh.points.len() {
            assert_eq!(mesh.points[i].id, sample.points[i].id);
            assert_eq!(mesh.points[i].coords, sample.points[i].coords);
        }
        Ok(())
    }
}
<|endoftext|>