<commit_before>//!Transparent base64 encoding / decoding as part of (de)serialization.

use std::{fmt, marker::PhantomData};

use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// A wrapper around `B` (usually `Vec<u8>`) that (de)serializes from / to a base64 string.
///
/// The base64 character set (and miscellaneous other encoding / decoding options) can be customized
/// through the generic parameter `C`.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Base64<C = Standard, B = Vec<u8>> {
    bytes: B,
    _phantom_conf: PhantomData<*mut C>,
}

/// Config used for the [`Base64`] type.
pub trait Base64Config {
    /// The config as a constant.
    ///
    /// Opaque so our interface is not tied to the base64 crate version.
    #[doc(hidden)]
    const CONF: Conf;
}

#[doc(hidden)]
pub struct Conf(base64::Config);

/// Standard base64 character set without padding.
///
/// Allows trailing bits in decoding for maximum compatibility.
#[non_exhaustive]
// Easier than implementing these all for Base64 manually to avoid the `C: Trait` bounds.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Standard;

impl Base64Config for Standard {
    // See https://github.com/matrix-org/matrix-doc/issues/3211
    const CONF: Conf = Conf(base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true));
}

/// Url-safe base64 character set without padding.
///
/// Allows trailing bits in decoding for maximum compatibility.
#[non_exhaustive]
// Easier than implementing these all for Base64 manually to avoid the `C: Trait` bounds.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct UrlSafe;

impl Base64Config for UrlSafe {
    const CONF: Conf = Conf(base64::URL_SAFE_NO_PAD.decode_allow_trailing_bits(true));
}

impl<C: Base64Config, B: AsRef<[u8]>> Base64<C, B> {
    /// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
    pub fn new(bytes: B) -> Self {
        Self { bytes, _phantom_conf: PhantomData }
    }

    /// Get a reference to the raw bytes held by this `Base64` instance.
    pub fn as_bytes(&self) -> &[u8] {
        self.bytes.as_ref()
    }

    /// Encode the bytes contained in this `Base64` instance to unpadded base64.
    pub fn encode(&self) -> String {
        base64::encode_config(&self.bytes, C::CONF.0)
    }
}

impl<C, B> Base64<C, B> {
    /// Get the raw bytes held by this `Base64` instance.
    pub fn into_inner(self) -> B {
        self.bytes
    }
}

impl<C: Base64Config> Base64<C> {
    /// Create a `Base64` instance containing an empty `Vec<u8>`.
    pub fn empty() -> Self {
        Self::new(Vec::new())
    }

    /// Parse some base64-encoded data to create a `Base64` instance.
    pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
        base64::decode_config(encoded, C::CONF.0).map(Self::new)
    }
}

impl<C: Base64Config, B: AsRef<[u8]>> fmt::Debug for Base64<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<C: Base64Config, B: AsRef<[u8]>> fmt::Display for Base64<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<'de, C: Base64Config> Deserialize<'de> for Base64<C> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let encoded = crate::deserialize_cow_str(deserializer)?;
        Self::parse(&*encoded).map_err(de::Error::custom)
    }
}

impl<C: Base64Config, B: AsRef<[u8]>> Serialize for Base64<C, B> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.encode())
    }
}
<commit_msg>serde: Fix Base64 not being Send + Sync<commit_after>//!Transparent base64 encoding / decoding as part of (de)serialization.

use std::{fmt, marker::PhantomData};

use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// A wrapper around `B` (usually `Vec<u8>`) that (de)serializes from / to a base64 string.
///
/// The base64 character set (and miscellaneous other encoding / decoding options) can be customized
/// through the generic parameter `C`.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Base64<C = Standard, B = Vec<u8>> {
    bytes: B,
    // Invariant PhantomData, Send + Sync
    _phantom_conf: PhantomData<fn(C) -> C>,
}

/// Config used for the [`Base64`] type.
pub trait Base64Config {
    /// The config as a constant.
    ///
    /// Opaque so our interface is not tied to the base64 crate version.
    #[doc(hidden)]
    const CONF: Conf;
}

#[doc(hidden)]
pub struct Conf(base64::Config);

/// Standard base64 character set without padding.
///
/// Allows trailing bits in decoding for maximum compatibility.
#[non_exhaustive]
// Easier than implementing these all for Base64 manually to avoid the `C: Trait` bounds.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Standard;

impl Base64Config for Standard {
    // See https://github.com/matrix-org/matrix-doc/issues/3211
    const CONF: Conf = Conf(base64::STANDARD_NO_PAD.decode_allow_trailing_bits(true));
}

/// Url-safe base64 character set without padding.
///
/// Allows trailing bits in decoding for maximum compatibility.
#[non_exhaustive]
// Easier than implementing these all for Base64 manually to avoid the `C: Trait` bounds.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct UrlSafe;

impl Base64Config for UrlSafe {
    const CONF: Conf = Conf(base64::URL_SAFE_NO_PAD.decode_allow_trailing_bits(true));
}

impl<C: Base64Config, B: AsRef<[u8]>> Base64<C, B> {
    /// Create a `Base64` instance from raw bytes, to be base64-encoded in serialialization.
    pub fn new(bytes: B) -> Self {
        Self { bytes, _phantom_conf: PhantomData }
    }

    /// Get a reference to the raw bytes held by this `Base64` instance.
    pub fn as_bytes(&self) -> &[u8] {
        self.bytes.as_ref()
    }

    /// Encode the bytes contained in this `Base64` instance to unpadded base64.
    pub fn encode(&self) -> String {
        base64::encode_config(&self.bytes, C::CONF.0)
    }
}

impl<C, B> Base64<C, B> {
    /// Get the raw bytes held by this `Base64` instance.
    pub fn into_inner(self) -> B {
        self.bytes
    }
}

impl<C: Base64Config> Base64<C> {
    /// Create a `Base64` instance containing an empty `Vec<u8>`.
    pub fn empty() -> Self {
        Self::new(Vec::new())
    }

    /// Parse some base64-encoded data to create a `Base64` instance.
    pub fn parse(encoded: impl AsRef<[u8]>) -> Result<Self, base64::DecodeError> {
        base64::decode_config(encoded, C::CONF.0).map(Self::new)
    }
}

impl<C: Base64Config, B: AsRef<[u8]>> fmt::Debug for Base64<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<C: Base64Config, B: AsRef<[u8]>> fmt::Display for Base64<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.encode().fmt(f)
    }
}

impl<'de, C: Base64Config> Deserialize<'de> for Base64<C> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let encoded = crate::deserialize_cow_str(deserializer)?;
        Self::parse(&*encoded).map_err(de::Error::custom)
    }
}

impl<C: Base64Config, B: AsRef<[u8]>> Serialize for Base64<C, B> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.encode())
    }
}
<|endoftext|>


<commit_before>use libc;
use ram::entry;
use ram::tombstone::{TOMBSTONE_SIZE_U32, Tombstone};
use ram::cell;
use ram::cell::Cell;
use ram::chunk::Chunk;
use ram::entry::EntryMeta;
use std::sync::atomic::{AtomicUsize, AtomicU32, AtomicI64, AtomicBool, Ordering};
use std::collections::BTreeSet;
use std::fs::{File, remove_file, copy};
use std::io::BufWriter;
use std::io::prelude::*;
use std::io;
use std::path::Path;
use crc32c::crc32c;
use bifrost::utils::async_locks::{RwLock, RwLockReadGuard};
use parking_lot;

use super::cell::CellHeader;

pub const MAX_SEGMENT_SIZE_U32: u32 = 8 * 1024 * 1024;
pub const MAX_SEGMENT_SIZE: usize = MAX_SEGMENT_SIZE_U32 as usize;

pub struct Segment {
    pub id: u64,
    pub addr: usize,
    pub bound: usize,
    pub append_header: AtomicUsize,
    pub dead_space: AtomicU32,
    pub tombstones: AtomicU32,
    pub dead_tombstones: AtomicU32,
    pub last_tombstones_scanned: AtomicI64,
    pub references: AtomicUsize,
    pub backup_file_name: Option<String>,
    pub wal_file: Option<parking_lot::Mutex<BufWriter<File>>>,
    pub wal_file_name: Option<String>,
    pub archived: AtomicBool,
    pub dropped: AtomicBool
}

impl Segment {
    pub fn new(id: u64, size: usize,
               backup_storage: &Option<String>,
               wal_storage: &Option<String>) -> Segment {
        let buffer_ptr = unsafe { libc::malloc(size) as usize };
        let mut wal_file_name = None;
        let mut wal_file = None;
        if let Some(wal_storage) = wal_storage {
            let file_name = format!("{}/{}.log", wal_storage, id);
            let file = BufWriter::with_capacity(
                4096, // most common disk block size
                File::open(&file_name).unwrap()); // fast fail
            wal_file_name = Some(file_name);
            wal_file = Some(parking_lot::Mutex::new(file));
        }
        debug!("Creating new segment with id {}, size {}, address {}", id, size, buffer_ptr);
        Segment {
            addr: buffer_ptr,
            id,
            bound: buffer_ptr + size,
            append_header: AtomicUsize::new(buffer_ptr),
            dead_space: AtomicU32::new(0),
            tombstones: AtomicU32::new(0),
            dead_tombstones: AtomicU32::new(0),
            last_tombstones_scanned: AtomicI64::new(0),
            references: AtomicUsize::new(0),
            backup_file_name: backup_storage.clone().map(|path| format!("{}/{}.backup", path, id)),
            archived: AtomicBool::new(false),
            dropped: AtomicBool::new(false),
            wal_file,
            wal_file_name
        }
    }

    pub fn try_acquire(&self, size: u32) -> Option<usize> {
        let size = size as usize;
        loop {
            let curr_last = self.append_header.load(Ordering::SeqCst);
            let exp_last = curr_last + size;
            if exp_last > self.bound {
                return None;
            } else {
                if self.append_header.compare_and_swap(curr_last, exp_last, Ordering::SeqCst) != curr_last {
                    continue;
                } else {
                    return Some(curr_last);
                }
            }
        }
    }

    fn append_header(&self) -> usize {
        self.append_header.load(Ordering::Relaxed)
    }

    pub fn entry_iter(&self) -> SegmentEntryIter {
        SegmentEntryIter {
            bound: self.append_header(),
            cursor: self.addr
        }
    }

    pub fn dead_space(&self) -> u32 {
        self.dead_space.load(Ordering::Relaxed)
    }

    // dead space plus tombstone spaces
    pub fn total_dead_space(&self) -> u32 {
        let dead_tombstones_space = self.dead_tombstones.load(Ordering::Relaxed) * TOMBSTONE_SIZE_U32;
        let dead_cells_space = self.dead_space();
        return dead_tombstones_space + dead_cells_space;
    }

    pub fn used_spaces(&self) -> u32 {
        return (self.append_header.load(Ordering::Relaxed) as usize - self.addr) as u32
    }

    pub fn living_space(&self) -> u32 {
        let total_dead_space = self.total_dead_space();
        let used_space = self.used_spaces();
        if total_dead_space <= used_space {
            used_space - total_dead_space
        } else {
            warn!("living space check error for segment {}, used {}, dead {}",
                  self.id, used_space, total_dead_space);
            0
        }
    }

    pub fn valid_space(&self) -> u32 {
        return self.used_spaces() - self.dead_space();
    }

    pub fn living_rate(&self) -> f32 {
        let used_space = self.used_spaces() as f32;
        if used_space == 0f32 {
            // empty segment
            return 1f32;
        }
        return self.living_space() as f32 / used_space;
    }

    // archive this segment and write the data to backup storage
    pub fn archive(&self) -> Result<bool, io::Error> {
        if let &Some(ref backup_file) = &self.backup_file_name {
            while !self.no_references() { /* wait until all references released */}
            let backup_file_path = Path::new(backup_file);
            if backup_file_path.exists() {
                warn!("Segment backup {} exists and can't archive twice", backup_file);
                return Ok(false);
            }
            if let Some(ref wal_file) = self.wal_file_name {
                // if there is a WAL file ready, copy this file to backup
                if let Some(ref file_mutex) = self.wal_file {
                    // this should be redundant but I don't want to take the chance
                    // obtain the writer lock before continue
                    let _ = file_mutex.lock();
                    copy(wal_file, backup_file);
                    remove_file(wal_file);
                } else { panic!() }

            } else {
                let backup_file = File::open(backup_file_path)?;
                let seg_size = self.append_header.load(Ordering::Relaxed) - self.addr;
                let mut buffer = BufWriter::with_capacity(seg_size, backup_file);
                unsafe {
                    for offset in 0..seg_size {
                        let ptr = (self.addr + offset) as *const u8;
                        let byte = *ptr;
                        buffer.write(&[byte])?;
                    }
                }
                buffer.flush()?;
                return Ok(true);
            }
        }
        return Ok(false);
    }

    pub fn write_wal(&self, addr: usize, size: u32) -> io::Result<()> {
        if let Some(ref wal_file) = self.wal_file {
            let mut file = wal_file.lock();
            unsafe {
                for offset in 0..size as usize {
                    let ptr = (addr + offset) as *const u8;
                    let byte = *ptr;
                    file.write(&[byte])?;
                }
            }
            file.flush()?;
        }
        return Ok(());
    }

    pub fn no_references(&self) -> bool { self.references.load(Ordering::Relaxed) == 0 }

    pub fn mem_drop(&self) {
        if !self.dropped.compare_and_swap(false, true, Ordering::Relaxed) {
            debug!("disposing segment at {}", self.addr);
            unsafe {
                libc::free(self.addr as *mut libc::c_void)
            }
        }
    }

    // remove the backup if it have one
    pub fn dispense(&self) {
        debug!("dispense segment {}", self.id);
        if let &Some(ref backup_storage) = &self.backup_file_name {
            let path = Path::new(backup_storage);
            if path.exists() {
                if let Err(e) = remove_file(path) {
                    error!("cannot reclaim segment file on dispense {}", backup_storage)
                }
            } else {
                error!("cannot find segment backup {}", backup_storage)
            }
        }
    }
}

impl Drop for Segment {
    fn drop(&mut self) {
        debug!("Memory dropping segment {}", self.id);
        assert_eq!(self.references.load(Ordering::Relaxed), 0);
        self.mem_drop()
    }
}

pub struct SegmentEntryIter {
    bound: usize,
    cursor: usize
}

impl Iterator for SegmentEntryIter {
    type Item = EntryMeta;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        let cursor = self.cursor;
        if cursor >= self.bound {
            return None;
        }
        let (_, entry_meta) = entry::Entry::decode_from(
            cursor,
            |body_pos, header| {
                let entry_header_size = body_pos - cursor;
                let entry_size = entry_header_size + header.content_length as usize;
                debug!("Found body pos {}, entry header {:?}. Header size: {}, entry size: {}, entry pos: {}, content length {}, bound {}",
                       body_pos, header, entry_header_size, entry_size, cursor, header.content_length, self.bound);
                return EntryMeta {
                    body_pos, entry_header: header, entry_size, entry_pos: cursor
                };
            });
        self.cursor += entry_meta.entry_size;
        Some(entry_meta)
    }
}<commit_msg>assert on userd spaces<commit_after>use libc;
use ram::entry;
use ram::tombstone::{TOMBSTONE_SIZE_U32, Tombstone};
use ram::cell;
use ram::cell::Cell;
use ram::chunk::Chunk;
use ram::entry::EntryMeta;
use std::sync::atomic::{AtomicUsize, AtomicU32, AtomicI64, AtomicBool, Ordering};
use std::collections::BTreeSet;
use std::fs::{File, remove_file, copy};
use std::io::BufWriter;
use std::io::prelude::*;
use std::io;
use std::path::Path;
use crc32c::crc32c;
use bifrost::utils::async_locks::{RwLock, RwLockReadGuard};
use parking_lot;

use super::cell::CellHeader;

pub const MAX_SEGMENT_SIZE_U32: u32 = 8 * 1024 * 1024;
pub const MAX_SEGMENT_SIZE: usize = MAX_SEGMENT_SIZE_U32 as usize;

pub struct Segment {
    pub id: u64,
    pub addr: usize,
    pub bound: usize,
    pub append_header: AtomicUsize,
    pub dead_space: AtomicU32,
    pub tombstones: AtomicU32,
    pub dead_tombstones: AtomicU32,
    pub last_tombstones_scanned: AtomicI64,
    pub references: AtomicUsize,
    pub backup_file_name: Option<String>,
    pub wal_file: Option<parking_lot::Mutex<BufWriter<File>>>,
    pub wal_file_name: Option<String>,
    pub archived: AtomicBool,
    pub dropped: AtomicBool
}

impl Segment {
    pub fn new(id: u64, size: usize,
               backup_storage: &Option<String>,
               wal_storage: &Option<String>) -> Segment {
        let buffer_ptr = unsafe { libc::malloc(size) as usize };
        let mut wal_file_name = None;
        let mut wal_file = None;
        if let Some(wal_storage) = wal_storage {
            let file_name = format!("{}/{}.log", wal_storage, id);
            let file = BufWriter::with_capacity(
                4096, // most common disk block size
                File::open(&file_name).unwrap()); // fast fail
            wal_file_name = Some(file_name);
            wal_file = Some(parking_lot::Mutex::new(file));
        }
        debug!("Creating new segment with id {}, size {}, address {}", id, size, buffer_ptr);
        Segment {
            addr: buffer_ptr,
            id,
            bound: buffer_ptr + size,
            append_header: AtomicUsize::new(buffer_ptr),
            dead_space: AtomicU32::new(0),
            tombstones: AtomicU32::new(0),
            dead_tombstones: AtomicU32::new(0),
            last_tombstones_scanned: AtomicI64::new(0),
            references: AtomicUsize::new(0),
            backup_file_name: backup_storage.clone().map(|path| format!("{}/{}.backup", path, id)),
            archived: AtomicBool::new(false),
            dropped: AtomicBool::new(false),
            wal_file,
            wal_file_name
        }
    }

    pub fn try_acquire(&self, size: u32) -> Option<usize> {
        let size = size as usize;
        loop {
            let curr_last = self.append_header.load(Ordering::SeqCst);
            let exp_last = curr_last + size;
            if exp_last > self.bound {
                return None;
            } else {
                if self.append_header.compare_and_swap(curr_last, exp_last, Ordering::SeqCst) != curr_last {
                    continue;
                } else {
                    return Some(curr_last);
                }
            }
        }
    }

    fn append_header(&self) -> usize {
        self.append_header.load(Ordering::Relaxed)
    }

    pub fn entry_iter(&self) -> SegmentEntryIter {
        SegmentEntryIter {
            bound: self.append_header(),
            cursor: self.addr
        }
    }

    pub fn dead_space(&self) -> u32 {
        self.dead_space.load(Ordering::Relaxed)
    }

    // dead space plus tombstone spaces
    pub fn total_dead_space(&self) -> u32 {
        let dead_tombstones_space = self.dead_tombstones.load(Ordering::Relaxed) * TOMBSTONE_SIZE_U32;
        let dead_cells_space = self.dead_space();
        return dead_tombstones_space + dead_cells_space;
    }

    pub fn used_spaces(&self) -> u32 {
        let space = (self.append_header.load(Ordering::Relaxed) as usize - self.addr);
        debug_assert!(space <= MAX_SEGMENT_SIZE);
        return space as u32
    }

    pub fn living_space(&self) -> u32 {
        let total_dead_space = self.total_dead_space();
        let used_space = self.used_spaces();
        if total_dead_space <= used_space {
            used_space - total_dead_space
        } else {
            warn!("living space check error for segment {}, used {}, dead {}",
                  self.id, used_space, total_dead_space);
            0
        }
    }

    pub fn valid_space(&self) -> u32 {
        return self.used_spaces() - self.dead_space();
    }

    pub fn living_rate(&self) -> f32 {
        let used_space = self.used_spaces() as f32;
        if used_space == 0f32 {
            // empty segment
            return 1f32;
        }
        return self.living_space() as f32 / used_space;
    }

    // archive this segment and write the data to backup storage
    pub fn archive(&self) -> Result<bool, io::Error> {
        if let &Some(ref backup_file) = &self.backup_file_name {
            while !self.no_references() { /* wait until all references released */}
            let backup_file_path = Path::new(backup_file);
            if backup_file_path.exists() {
                warn!("Segment backup {} exists and can't archive twice", backup_file);
                return Ok(false);
            }
            if let Some(ref wal_file) = self.wal_file_name {
                // if there is a WAL file ready, copy this file to backup
                if let Some(ref file_mutex) = self.wal_file {
                    // this should be redundant but I don't want to take the chance
                    // obtain the writer lock before continue
                    let _ = file_mutex.lock();
                    copy(wal_file, backup_file);
                    remove_file(wal_file);
                } else { panic!() }

            } else {
                let backup_file = File::open(backup_file_path)?;
                let seg_size = self.append_header.load(Ordering::Relaxed) - self.addr;
                let mut buffer = BufWriter::with_capacity(seg_size, backup_file);
                unsafe {
                    for offset in 0..seg_size {
                        let ptr = (self.addr + offset) as *const u8;
                        let byte = *ptr;
                        buffer.write(&[byte])?;
                    }
                }
                buffer.flush()?;
                return Ok(true);
            }
        }
        return Ok(false);
    }

    pub fn write_wal(&self, addr: usize, size: u32) -> io::Result<()> {
        if let Some(ref wal_file) = self.wal_file {
            let mut file = wal_file.lock();
            unsafe {
                for offset in 0..size as usize {
                    let ptr = (addr + offset) as *const u8;
                    let byte = *ptr;
                    file.write(&[byte])?;
                }
            }
            file.flush()?;
        }
        return Ok(());
    }

    pub fn no_references(&self) -> bool { self.references.load(Ordering::Relaxed) == 0 }

    pub fn mem_drop(&self) {
        if !self.dropped.compare_and_swap(false, true, Ordering::Relaxed) {
            debug!("disposing segment at {}", self.addr);
            unsafe {
                libc::free(self.addr as *mut libc::c_void)
            }
        }
    }

    // remove the backup if it have one
    pub fn dispense(&self) {
        debug!("dispense segment {}", self.id);
        if let &Some(ref backup_storage) = &self.backup_file_name {
            let path = Path::new(backup_storage);
            if path.exists() {
                if let Err(e) = remove_file(path) {
                    error!("cannot reclaim segment file on dispense {}", backup_storage)
                }
            } else {
                error!("cannot find segment backup {}", backup_storage)
            }
        }
    }
}

impl Drop for Segment {
    fn drop(&mut self) {
        debug!("Memory dropping segment {}", self.id);
        assert_eq!(self.references.load(Ordering::Relaxed), 0);
        self.mem_drop()
    }
}

pub struct SegmentEntryIter {
    bound: usize,
    cursor: usize
}

impl Iterator for SegmentEntryIter {
    type Item = EntryMeta;

    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        let cursor = self.cursor;
        if cursor >= self.bound {
            return None;
        }
        let (_, entry_meta) = entry::Entry::decode_from(
            cursor,
            |body_pos, header| {
                let entry_header_size = body_pos - cursor;
                let entry_size = entry_header_size + header.content_length as usize;
                debug!("Found body pos {}, entry header {:?}. Header size: {}, entry size: {}, entry pos: {}, content length {}, bound {}",
                       body_pos, header, entry_header_size, entry_size, cursor, header.content_length, self.bound);
                return EntryMeta {
                    body_pos, entry_header: header, entry_size, entry_pos: cursor
                };
            });
        self.cursor += entry_meta.entry_size;
        Some(entry_meta)
    }
}<|endoftext|>


<commit_before>use config::{Config, Environment, File};
use std::path::MAIN_SEPARATOR;

#[cfg(target_os = "windows")]
const CFG_FILE: &str = "ctehxk2";
#[cfg(not(target_os = "windows"))]
const CFG_FILE: &str = "libctehxk2";

#[derive(Debug, Deserialize)]
pub struct Settings {
    timeout: u64,
    base_url: String,
    log_level: String,
    log_path: Option<String>,
    ctn: Option<u16>,
    pn: Option<u16>,
}

impl Settings {
    fn new() -> Self {
        let mut settings = Config::new();

        // set defaults
        settings
            .set_default("base_url", "http://localhost:8080/k2/ctapi/")
            .expect("Failed to set default for base_url!")
            .set_default("log_level", "Error")
            .expect("Failed to set default for log_level!")
            .set_default("timeout", 5000)
            .expect("Failed set default for timeout!");

        // merge with optional config file and env variables
        settings
            .merge(File::with_name(CFG_FILE).required(false))
            .expect("Failed to merge config file!")
            .merge(Environment::with_prefix("k2"))
            .expect("Failed to merge env variables!");

        settings.try_into().expect("Failed to create configuration")
    }

    pub fn base_url() -> String {
        let s = Settings::new();
        let mut url = s.base_url.clone();
        if !url.trim().ends_with("/") {
            url.push_str("/");
        }
        url
    }

    pub fn ctn_or(fallback: u16) -> u16 {
        let s = Settings::new();
        match s.pn {
            Some(_pn) => match s.ctn {
                Some(ctn) => ctn,
                None => fallback,
            },
            None => fallback,
        }
    }

    pub fn pn_or(fallback: u16) -> u16 {
        let s = Settings::new();
        match s.ctn {
            Some(_ctn) => match s.pn {
                Some(pn) => pn,
                None => fallback,
            },
            None => fallback,
        }
    }

    pub fn log_level() -> String {
        let s = Settings::new();
        s.log_level.clone()
    }

    pub fn log_path() -> Option<String> {
        let s = Settings::new();
        match s.log_path {
            Some(mut path) => {
                if !path.trim().ends_with(MAIN_SEPARATOR) {
                    path.push(MAIN_SEPARATOR);
                }

                Some(path)
            }
            None => None,
        }
    }

    pub fn timeout() -> u64 {
        let s = Settings::new();
        s.timeout.clone()
    }
}

#[cfg(test)]
mod tests {
    use super::Settings;
    use std::env;
    use std::path::MAIN_SEPARATOR;

    #[test]
    fn ctn_or_return_env_value_if_pn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");

        let ctn = Settings::ctn_or(1);

        assert_eq!(ctn, 2);
    }

    #[test]
    fn ctn_or_return_given_value_if_pn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");

        let ctn = Settings::ctn_or(1);

        assert_eq!(ctn, 1);

        env::set_var("K2_CTN", "2");

        let ctn = Settings::ctn_or(1);

        assert_eq!(ctn, 1);
    }

    #[test]
    fn pn_or_return_env_value_if_ctn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");

        let pn = Settings::pn_or(1);

        assert_eq!(pn, 4);
    }

    #[test]
    fn pn_or_return_given_value_if_ctn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");

        let pn = Settings::pn_or(1);

        assert_eq!(pn, 1);

        env::set_var("K2_PN", "2");

        let pn = Settings::pn_or(1);

        assert_eq!(pn, 1);
    }

    #[test]
    fn base_url_return_env_value() {
        env::set_var("K2_BASE_URL", "a/");

        let url = Settings::base_url();

        assert_eq!(url, "a/");

        env::set_var("K2_BASE_URL", "1");

        let url = Settings::base_url();

        assert_eq!(url, "1/");
    }

    #[test]
    fn base_url_return_default_value_if_no_env() {
        env::remove_var("K2_BASE_URL");

        let url = Settings::base_url();

        assert_eq!(url, "http://localhost:8080/k2/ctapi/");
    }

    #[test]
    fn log_path_returns_env_value() {
        env::set_var("K2_LOG_PATH", "a");

        let path = Settings::log_path();

        assert_eq!(path, Some(format!("a{}", MAIN_SEPARATOR)));
    }

    #[test]
    fn log_path_return_none_if_no_env() {
        env::remove_var("K2_LOG_PATH");

        let path = Settings::log_path();

        assert!(path.is_none());
    }

    #[test]
    fn timeout_returns_env_value() {
        env::set_var("K2_TIMEOUT", "42");

        let timeout = Settings::timeout();

        assert_eq!(timeout, 42);
    }

    #[test]
    fn timeout_return_default_if_no_env() {
        env::remove_var("K2_TIMEOUT");

        let timeout = Settings::timeout();

        assert_eq!(timeout, 5000);
    }
}
<commit_msg>make timeout optional and per default disabled<commit_after>use config::{Config, Environment, File};
use std::path::MAIN_SEPARATOR;

#[cfg(target_os = "windows")]
const CFG_FILE: &str = "ctehxk2";
#[cfg(not(target_os = "windows"))]
const CFG_FILE: &str = "libctehxk2";

#[derive(Debug, Deserialize)]
pub struct Settings {
    timeout: Option<u64>,
    base_url: String,
    log_level: String,
    log_path: Option<String>,
    ctn: Option<u16>,
    pn: Option<u16>,
}

impl Settings {
    fn new() -> Self {
        let mut settings = Config::new();

        // set defaults
        settings
            .set_default("base_url", "http://localhost:8080/k2/ctapi/")
            .expect("Failed to set default for base_url!")
            .set_default("log_level", "Error")
            .expect("Failed to set default for log_level!");

        // merge with optional config file and env variables
        settings
            .merge(File::with_name(CFG_FILE).required(false))
            .expect("Failed to merge config file!")
            .merge(Environment::with_prefix("k2"))
            .expect("Failed to merge env variables!");

        settings.try_into().expect("Failed to create configuration")
    }

    pub fn base_url() -> String {
        let s = Settings::new();
        let mut url = s.base_url.clone();
        if !url.trim().ends_with("/") {
            url.push_str("/");
        }
        url
    }

    pub fn ctn_or(fallback: u16) -> u16 {
        let s = Settings::new();
        match s.pn {
            Some(_pn) => match s.ctn {
                Some(ctn) => ctn,
                None => fallback,
            },
            None => fallback,
        }
    }

    pub fn pn_or(fallback: u16) -> u16 {
        let s = Settings::new();
        match s.ctn {
            Some(_ctn) => match s.pn {
                Some(pn) => pn,
                None => fallback,
            },
            None => fallback,
        }
    }

    pub fn log_level() -> String {
        let s = Settings::new();
        s.log_level.clone()
    }

    pub fn log_path() -> Option<String> {
        let s = Settings::new();
        match s.log_path {
            Some(mut path) => {
                if !path.trim().ends_with(MAIN_SEPARATOR) {
                    path.push(MAIN_SEPARATOR);
                }

                Some(path)
            }
            None => None,
        }
    }

    pub fn timeout() -> Option<u64> {
        let s = Settings::new();
        match s.timeout {
            Some(timeout) => match timeout {
                0 => None,
                _ => Some(timeout)
            }
            None => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Settings;
    use std::env;
    use std::path::MAIN_SEPARATOR;

    #[test]
    fn ctn_or_return_env_value_if_pn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");

        let ctn = Settings::ctn_or(1);

        assert_eq!(ctn, 2);
    }

    #[test]
    fn ctn_or_return_given_value_if_pn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");

        let ctn = Settings::ctn_or(1);

        assert_eq!(ctn, 1);

        env::set_var("K2_CTN", "2");

        let ctn = Settings::ctn_or(1);

        assert_eq!(ctn, 1);
    }

    #[test]
    fn pn_or_return_env_value_if_ctn_is_set_too() {
        env::set_var("K2_CTN", "2");
        env::set_var("K2_PN", "4");

        let pn = Settings::pn_or(1);

        assert_eq!(pn, 4);
    }

    #[test]
    fn pn_or_return_given_value_if_ctn_not_set() {
        env::remove_var("K2_CTN");
        env::remove_var("K2_PN");

        let pn = Settings::pn_or(1);

        assert_eq!(pn, 1);

        env::set_var("K2_PN", "2");

        let pn = Settings::pn_or(1);

        assert_eq!(pn, 1);
    }

    #[test]
    fn base_url_return_env_value() {
        env::set_var("K2_BASE_URL", "a/");

        let url = Settings::base_url();

        assert_eq!(url, "a/");

        env::set_var("K2_BASE_URL", "1");

        let url = Settings::base_url();

        assert_eq!(url, "1/");
    }

    #[test]
    fn base_url_return_default_value_if_no_env() {
        env::remove_var("K2_BASE_URL");

        let url = Settings::base_url();

        assert_eq!(url, "http://localhost:8080/k2/ctapi/");
    }

    #[test]
    fn log_path_returns_env_value() {
        env::set_var("K2_LOG_PATH", "a");

        let path = Settings::log_path();

        assert_eq!(path, Some(format!("a{}", MAIN_SEPARATOR)));
    }

    #[test]
    fn log_path_return_none_if_no_env() {
        env::remove_var("K2_LOG_PATH");

        let path = Settings::log_path();

        assert!(path.is_none());
    }

    #[test]
    fn timeout_returns_env_value() {
        env::set_var("K2_TIMEOUT", "42");

        let timeout = Settings::timeout();

        assert_eq!(timeout, Some(42));
    }

    #[test]
    fn timeout_return_none_if_no_env() {
        env::remove_var("K2_TIMEOUT");

        let timeout = Settings::timeout();

        assert!(timeout.is_none());
    }
}
<|endoftext|>


<commit_before>//! A single-producer, multi-consumer channel that only retains the *last* sent
//! value.
//!
//! This channel is useful for watching for changes to a value from multiple
//! points in the code base, for example, changes to configuration values.
//!
//! # Usage
//!
//! [`channel`] returns a [`Sender`] / [`Receiver`] pair. These are
//! the producer and sender halves of the channel. The channel is
//! created with an initial value. [`Receiver::recv`] will always
//! be ready upon creation and will yield either this initial value or
//! the latest value that has been sent by `Sender`.
//!
//! Calls to [`Receiver::recv`] will always yield the latest value.
//!
//! # Examples
//!
//! ```
//! use tokio::sync::watch;
//!
//! # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
//!     let (tx, mut rx) = watch::channel("hello");
//!
//!     tokio::spawn(async move {
//!         while let Some(value) = rx.recv().await {
//!             println!("received = {:?}", value);
//!         }
//!     });
//!
//!     tx.broadcast("world")?;
//! # Ok(())
//! # }
//! ```
//!
//! # Closing
//!
//! [`Sender::closed`] allows the producer to detect when all [`Receiver`]
//! handles have been dropped. This indicates that there is no further interest
//! in the values being produced and work can be stopped.
//!
//! # Thread safety
//!
//! Both [`Sender`] and [`Receiver`] are thread safe. They can be moved to other
//! threads and can be used in a concurrent environment. Clones of [`Receiver`]
//! handles may be moved to separate threads and also used concurrently.
//!
//! [`Sender`]: crate::sync::watch::Sender
//! [`Receiver`]: crate::sync::watch::Receiver
//! [`Receiver::recv`]: crate::sync::watch::Receiver::recv
//! [`channel`]: crate::sync::watch::channel
//! [`Sender::closed`]: crate::sync::watch::Sender::closed

use crate::future::poll_fn;
use crate::sync::task::AtomicWaker;

use fnv::FnvHashMap;
use std::ops;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, Weak};
use std::task::Poll::{Pending, Ready};
use std::task::{Context, Poll};

/// Receives values from the associated [`Sender`](struct.Sender.html).
///
/// Instances are created by the [`channel`](fn.channel.html) function.
#[derive(Debug)]
pub struct Receiver<T> {
    /// Pointer to the shared state
    shared: Arc<Shared<T>>,

    /// Pointer to the watcher's internal state
    inner: Arc<WatchInner>,

    /// Watcher ID.
    id: u64,

    /// Last observed version
    ver: usize,
}

/// Sends values to the associated [`Receiver`](struct.Receiver.html).
///
/// Instances are created by the [`channel`](fn.channel.html) function.
#[derive(Debug)]
pub struct Sender<T> {
    shared: Weak<Shared<T>>,
}

/// Returns a reference to the inner value
///
/// Outstanding borrows hold a read lock on the inner value. This means that
/// long lived borrows could cause the produce half to block. It is recommended
/// to keep the borrow as short lived as possible.
#[derive(Debug)]
pub struct Ref<'a, T> {
    inner: RwLockReadGuard<'a, T>,
}

pub mod error {
    //! Watch error types

    use std::fmt;

    /// Error produced when sending a value fails.
    #[derive(Debug)]
    pub struct SendError<T> {
        pub(crate) inner: T,
    }

    // ===== impl SendError =====

    impl<T: fmt::Debug> fmt::Display for SendError<T> {
        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(fmt, "channel closed")
        }
    }

    impl<T: fmt::Debug> std::error::Error for SendError<T> {}
}

#[derive(Debug)]
struct Shared<T> {
    /// The most recent value
    value: RwLock<T>,

    /// The current version
    ///
    /// The lowest bit represents a "closed" state. The rest of the bits
    /// represent the current version.
    version: AtomicUsize,

    /// All watchers
    watchers: Mutex<Watchers>,

    /// Task to notify when all watchers drop
    cancel: AtomicWaker,
}

#[derive(Debug)]
struct Watchers {
    next_id: u64,
    watchers: FnvHashMap<u64, Arc<WatchInner>>,
}

#[derive(Debug)]
struct WatchInner {
    waker: AtomicWaker,
}

const CLOSED: usize = 1;

/// Creates a new watch channel, returning the "send" and "receive" handles.
///
/// All values sent by [`Sender`] will become visible to the [`Receiver`] handles.
/// Only the last value sent is made available to the [`Receiver`] half. All
/// intermediate values are dropped.
///
/// # Examples
///
/// ```
/// use tokio::sync::watch;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
///     let (tx, mut rx) = watch::channel("hello");
///
///     tokio::spawn(async move {
///         while let Some(value) = rx.recv().await {
///             println!("received = {:?}", value);
///         }
///     });
///
///     tx.broadcast("world")?;
/// # Ok(())
/// # }
/// ```
///
/// [`Sender`]: struct.Sender.html
/// [`Receiver`]: struct.Receiver.html
pub fn channel<T: Clone>(init: T) -> (Sender<T>, Receiver<T>) {
    const INIT_ID: u64 = 0;

    let inner = Arc::new(WatchInner::new());

    // Insert the watcher
    let mut watchers = FnvHashMap::with_capacity_and_hasher(0, Default::default());
    watchers.insert(INIT_ID, inner.clone());

    let shared = Arc::new(Shared {
        value: RwLock::new(init),
        version: AtomicUsize::new(2),
        watchers: Mutex::new(Watchers {
            next_id: INIT_ID + 1,
            watchers,
        }),
        cancel: AtomicWaker::new(),
    });

    let tx = Sender {
        shared: Arc::downgrade(&shared),
    };

    let rx = Receiver {
        shared,
        inner,
        id: INIT_ID,
        ver: 0,
    };

    (tx, rx)
}

impl<T> Receiver<T> {
    /// Returns a reference to the most recently sent value
    ///
    /// Outstanding borrows hold a read lock. This means that long lived borrows
    /// could cause the send half to block. It is recommended to keep the borrow
    /// as short lived as possible.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;
    ///
    /// let (_, rx) = watch::channel("hello");
    /// assert_eq!(*rx.borrow(), "hello");
    /// ```
    pub fn borrow(&self) -> Ref<'_, T> {
        let inner = self.shared.value.read().unwrap();
        Ref { inner }
    }

    // TODO: document
    #[doc(hidden)]
    pub fn poll_recv_ref<'a>(&'a mut self, cx: &mut Context<'_>) -> Poll<Option<Ref<'a, T>>> {
        // Make sure the task is up to date
        self.inner.waker.register_by_ref(cx.waker());

        let state = self.shared.version.load(SeqCst);
        let version = state & !CLOSED;

        if version != self.ver {
            let inner = self.shared.value.read().unwrap();
            self.ver = version;

            return Ready(Some(Ref { inner }));
        }

        if CLOSED == state & CLOSED {
            // The `Store` handle has been dropped.
            return Ready(None);
        }

        Pending
    }
}

impl<T: Clone> Receiver<T> {
    /// Attempts to clone the latest value sent via the channel.
    ///
    /// If this is the first time the function is called on a `Receiver`
    /// instance, then the function completes immediately with the **current**
    /// value held by the channel. On the next call, the function waits until
    /// a new value is sent in the channel.
    ///
    /// `None` is returned if the `Sender` half is dropped.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let (tx, mut rx) = watch::channel("hello");
    ///
    ///     let v = rx.recv().await.unwrap();
    ///     assert_eq!(v, "hello");
    ///
    ///     tokio::spawn(async move {
    ///         tx.broadcast("goodbye").unwrap();
    ///     });
    ///
    ///     // Waits for the new task to spawn and send the value.
    ///     let v = rx.recv().await.unwrap();
    ///     assert_eq!(v, "goodbye");
    ///
    ///     let v = rx.recv().await;
    ///     assert!(v.is_none());
    /// }
    /// ```
    pub async fn recv(&mut self) -> Option<T> {
        poll_fn(|cx| {
            let v_ref = ready!(self.poll_recv_ref(cx));
            Poll::Ready(v_ref.map(|v_ref| (*v_ref).clone()))
        })
        .await
    }
}

#[cfg(feature = "stream")]
impl<T: Clone> crate::stream::Stream for Receiver<T> {
    type Item = T;

    fn poll_next(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
        let v_ref = ready!(self.poll_recv_ref(cx));

        Poll::Ready(v_ref.map(|v_ref| (*v_ref).clone()))
    }
}

impl<T> Clone for Receiver<T> {
    fn clone(&self) -> Self {
        let inner = Arc::new(WatchInner::new());
        let shared = self.shared.clone();

        let id = {
            let mut watchers = shared.watchers.lock().unwrap();
            let id = watchers.next_id;

            watchers.next_id += 1;
            watchers.watchers.insert(id, inner.clone());

            id
        };

        let ver = self.ver;

        Receiver {
            shared,
            inner,
            id,
            ver,
        }
    }
}

impl<T> Drop for Receiver<T> {
    fn drop(&mut self) {
        let mut watchers = self.shared.watchers.lock().unwrap();
        watchers.watchers.remove(&self.id);
    }
}

impl WatchInner {
    fn new() -> Self {
        WatchInner {
            waker: AtomicWaker::new(),
        }
    }
}

impl<T> Sender<T> {
    /// Broadcasts a new value via the channel, notifying all receivers.
    pub fn broadcast(&self, value: T) -> Result<(), error::SendError<T>> {
        let shared = match self.shared.upgrade() {
            Some(shared) => shared,
            // All `Watch` handles have been canceled
            None => return Err(error::SendError { inner: value }),
        };

        // Replace the value
        {
            let mut lock = shared.value.write().unwrap();
            *lock = value;
        }

        // Update the version. 2 is used so that the CLOSED bit is not set.
        shared.version.fetch_add(2, SeqCst);

        // Notify all watchers
        notify_all(&*shared);

        // Return the old value
        Ok(())
    }

    /// Completes when all receivers have dropped.
    ///
    /// This allows the producer to get notified when interest in the produced
    /// values is canceled and immediately stop doing work.
    pub async fn closed(&mut self) {
        poll_fn(|cx| self.poll_close(cx)).await
    }

    fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll<()> {
        match self.shared.upgrade() {
            Some(shared) => {
                shared.cancel.register_by_ref(cx.waker());
                Pending
            }
            None => Ready(()),
        }
    }
}

/// Notifies all watchers of a change
fn notify_all<T>(shared: &Shared<T>) {
    let watchers = shared.watchers.lock().unwrap();

    for watcher in watchers.watchers.values() {
        // Notify the task
        watcher.waker.wake();
    }
}

impl<T> Drop for Sender<T> {
    fn drop(&mut self) {
        if let Some(shared) = self.shared.upgrade() {
            shared.version.fetch_or(CLOSED, SeqCst);
            notify_all(&*shared);
        }
    }
}

// ===== impl Ref =====

impl<T> ops::Deref for Ref<'_, T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner.deref()
    }
}

// ===== impl Shared =====

impl<T> Drop for Shared<T> {
    fn drop(&mut self) {
        self.cancel.wake();
    }
}
<commit_msg>sync: reduce memory size of watch::Receiver (#2191)<commit_after>//! A single-producer, multi-consumer channel that only retains the *last* sent
//! value.
//!
//! This channel is useful for watching for changes to a value from multiple
//! points in the code base, for example, changes to configuration values.
//!
//! # Usage
//!
//! [`channel`] returns a [`Sender`] / [`Receiver`] pair. These are
//! the producer and sender halves of the channel. The channel is
//! created with an initial value. [`Receiver::recv`] will always
//! be ready upon creation and will yield either this initial value or
//! the latest value that has been sent by `Sender`.
//!
//! Calls to [`Receiver::recv`] will always yield the latest value.
//!
//! # Examples
//!
//! ```
//! use tokio::sync::watch;
//!
//! # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
//!     let (tx, mut rx) = watch::channel("hello");
//!
//!     tokio::spawn(async move {
//!         while let Some(value) = rx.recv().await {
//!             println!("received = {:?}", value);
//!         }
//!     });
//!
//!     tx.broadcast("world")?;
//! # Ok(())
//! # }
//! ```
//!
//! # Closing
//!
//! [`Sender::closed`] allows the producer to detect when all [`Receiver`]
//! handles have been dropped. This indicates that there is no further interest
//! in the values being produced and work can be stopped.
//!
//! # Thread safety
//!
//! Both [`Sender`] and [`Receiver`] are thread safe. They can be moved to other
//! threads and can be used in a concurrent environment. Clones of [`Receiver`]
//! handles may be moved to separate threads and also used concurrently.
//!
//! [`Sender`]: crate::sync::watch::Sender
//! [`Receiver`]: crate::sync::watch::Receiver
//! [`Receiver::recv`]: crate::sync::watch::Receiver::recv
//! [`channel`]: crate::sync::watch::channel
//! [`Sender::closed`]: crate::sync::watch::Sender::closed

use crate::future::poll_fn;
use crate::sync::task::AtomicWaker;

use fnv::FnvHashSet;
use std::ops;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{Relaxed, SeqCst};
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, Weak};
use std::task::Poll::{Pending, Ready};
use std::task::{Context, Poll};

/// Receives values from the associated [`Sender`](struct.Sender.html).
///
/// Instances are created by the [`channel`](fn.channel.html) function.
#[derive(Debug)]
pub struct Receiver<T> {
    /// Pointer to the shared state
    shared: Arc<Shared<T>>,

    /// Pointer to the watcher's internal state
    inner: Watcher,
}

/// Sends values to the associated [`Receiver`](struct.Receiver.html).
///
/// Instances are created by the [`channel`](fn.channel.html) function.
#[derive(Debug)]
pub struct Sender<T> {
    shared: Weak<Shared<T>>,
}

/// Returns a reference to the inner value
///
/// Outstanding borrows hold a read lock on the inner value. This means that
/// long lived borrows could cause the produce half to block. It is recommended
/// to keep the borrow as short lived as possible.
#[derive(Debug)]
pub struct Ref<'a, T> {
    inner: RwLockReadGuard<'a, T>,
}

pub mod error {
    //! Watch error types

    use std::fmt;

    /// Error produced when sending a value fails.
    #[derive(Debug)]
    pub struct SendError<T> {
        pub(crate) inner: T,
    }

    // ===== impl SendError =====

    impl<T: fmt::Debug> fmt::Display for SendError<T> {
        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(fmt, "channel closed")
        }
    }

    impl<T: fmt::Debug> std::error::Error for SendError<T> {}
}

#[derive(Debug)]
struct Shared<T> {
    /// The most recent value
    value: RwLock<T>,

    /// The current version
    ///
    /// The lowest bit represents a "closed" state. The rest of the bits
    /// represent the current version.
    version: AtomicUsize,

    /// All watchers
    watchers: Mutex<Watchers>,

    /// Task to notify when all watchers drop
    cancel: AtomicWaker,
}

type Watchers = FnvHashSet<Watcher>;

/// The watcher's ID is based on the Arc's pointer.
#[derive(Clone, Debug)]
struct Watcher(Arc<WatchInner>);

#[derive(Debug)]
struct WatchInner {
    /// Last observed version
    version: AtomicUsize,
    waker: AtomicWaker,
}

const CLOSED: usize = 1;

/// Creates a new watch channel, returning the "send" and "receive" handles.
///
/// All values sent by [`Sender`] will become visible to the [`Receiver`] handles.
/// Only the last value sent is made available to the [`Receiver`] half. All
/// intermediate values are dropped.
///
/// # Examples
///
/// ```
/// use tokio::sync::watch;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
///     let (tx, mut rx) = watch::channel("hello");
///
///     tokio::spawn(async move {
///         while let Some(value) = rx.recv().await {
///             println!("received = {:?}", value);
///         }
///     });
///
///     tx.broadcast("world")?;
/// # Ok(())
/// # }
/// ```
///
/// [`Sender`]: struct.Sender.html
/// [`Receiver`]: struct.Receiver.html
pub fn channel<T: Clone>(init: T) -> (Sender<T>, Receiver<T>) {
    const VERSION_0: usize = 0;
    const VERSION_1: usize = 2;

    // We don't start knowing VERSION_1
    let inner = Watcher::new_version(VERSION_0);

    // Insert the watcher
    let mut watchers = FnvHashSet::with_capacity_and_hasher(0, Default::default());
    watchers.insert(inner.clone());

    let shared = Arc::new(Shared {
        value: RwLock::new(init),
        version: AtomicUsize::new(VERSION_1),
        watchers: Mutex::new(watchers),
        cancel: AtomicWaker::new(),
    });

    let tx = Sender {
        shared: Arc::downgrade(&shared),
    };

    let rx = Receiver { shared, inner };

    (tx, rx)
}

impl<T> Receiver<T> {
    /// Returns a reference to the most recently sent value
    ///
    /// Outstanding borrows hold a read lock. This means that long lived borrows
    /// could cause the send half to block. It is recommended to keep the borrow
    /// as short lived as possible.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;
    ///
    /// let (_, rx) = watch::channel("hello");
    /// assert_eq!(*rx.borrow(), "hello");
    /// ```
    pub fn borrow(&self) -> Ref<'_, T> {
        let inner = self.shared.value.read().unwrap();
        Ref { inner }
    }

    // TODO: document
    #[doc(hidden)]
    pub fn poll_recv_ref<'a>(&'a mut self, cx: &mut Context<'_>) -> Poll<Option<Ref<'a, T>>> {
        // Make sure the task is up to date
        self.inner.waker.register_by_ref(cx.waker());

        let state = self.shared.version.load(SeqCst);
        let version = state & !CLOSED;

        if self.inner.version.swap(version, Relaxed) != version {
            let inner = self.shared.value.read().unwrap();

            return Ready(Some(Ref { inner }));
        }

        if CLOSED == state & CLOSED {
            // The `Store` handle has been dropped.
            return Ready(None);
        }

        Pending
    }
}

impl<T: Clone> Receiver<T> {
    /// Attempts to clone the latest value sent via the channel.
    ///
    /// If this is the first time the function is called on a `Receiver`
    /// instance, then the function completes immediately with the **current**
    /// value held by the channel. On the next call, the function waits until
    /// a new value is sent in the channel.
    ///
    /// `None` is returned if the `Sender` half is dropped.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::sync::watch;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let (tx, mut rx) = watch::channel("hello");
    ///
    ///     let v = rx.recv().await.unwrap();
    ///     assert_eq!(v, "hello");
    ///
    ///     tokio::spawn(async move {
    ///         tx.broadcast("goodbye").unwrap();
    ///     });
    ///
    ///     // Waits for the new task to spawn and send the value.
    ///     let v = rx.recv().await.unwrap();
    ///     assert_eq!(v, "goodbye");
    ///
    ///     let v = rx.recv().await;
    ///     assert!(v.is_none());
    /// }
    /// ```
    pub async fn recv(&mut self) -> Option<T> {
        poll_fn(|cx| {
            let v_ref = ready!(self.poll_recv_ref(cx));
            Poll::Ready(v_ref.map(|v_ref| (*v_ref).clone()))
        })
        .await
    }
}

#[cfg(feature = "stream")]
impl<T: Clone> crate::stream::Stream for Receiver<T> {
    type Item = T;

    fn poll_next(mut self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
        let v_ref = ready!(self.poll_recv_ref(cx));

        Poll::Ready(v_ref.map(|v_ref| (*v_ref).clone()))
    }
}

impl<T> Clone for Receiver<T> {
    fn clone(&self) -> Self {
        let ver = self.inner.version.load(Relaxed);
        let inner = Watcher::new_version(ver);
        let shared = self.shared.clone();

        shared.watchers.lock().unwrap().insert(inner.clone());

        Receiver { shared, inner }
    }
}

impl<T> Drop for Receiver<T> {
    fn drop(&mut self) {
        self.shared.watchers.lock().unwrap().remove(&self.inner);
    }
}

impl<T> Sender<T> {
    /// Broadcasts a new value via the channel, notifying all receivers.
    pub fn broadcast(&self, value: T) -> Result<(), error::SendError<T>> {
        let shared = match self.shared.upgrade() {
            Some(shared) => shared,
            // All `Watch` handles have been canceled
            None => return Err(error::SendError { inner: value }),
        };

        // Replace the value
        {
            let mut lock = shared.value.write().unwrap();
            *lock = value;
        }

        // Update the version. 2 is used so that the CLOSED bit is not set.
        shared.version.fetch_add(2, SeqCst);

        // Notify all watchers
        notify_all(&*shared);

        // Return the old value
        Ok(())
    }

    /// Completes when all receivers have dropped.
    ///
    /// This allows the producer to get notified when interest in the produced
    /// values is canceled and immediately stop doing work.
    pub async fn closed(&mut self) {
        poll_fn(|cx| self.poll_close(cx)).await
    }

    fn poll_close(&mut self, cx: &mut Context<'_>) -> Poll<()> {
        match self.shared.upgrade() {
            Some(shared) => {
                shared.cancel.register_by_ref(cx.waker());
                Pending
            }
            None => Ready(()),
        }
    }
}

/// Notifies all watchers of a change
fn notify_all<T>(shared: &Shared<T>) {
    let watchers = shared.watchers.lock().unwrap();

    for watcher in watchers.iter() {
        // Notify the task
        watcher.waker.wake();
    }
}

impl<T> Drop for Sender<T> {
    fn drop(&mut self) {
        if let Some(shared) = self.shared.upgrade() {
            shared.version.fetch_or(CLOSED, SeqCst);
            notify_all(&*shared);
        }
    }
}

// ===== impl Ref =====

impl<T> ops::Deref for Ref<'_, T> {
    type Target = T;

    fn deref(&self) -> &T {
        self.inner.deref()
    }
}

// ===== impl Shared =====

impl<T> Drop for Shared<T> {
    fn drop(&mut self) {
        self.cancel.wake();
    }
}

// ===== impl Watcher =====

impl Watcher {
    fn new_version(version: usize) -> Self {
        Watcher(Arc::new(WatchInner {
            version: AtomicUsize::new(version),
            waker: AtomicWaker::new(),
        }))
    }
}

impl std::cmp::PartialEq for Watcher {
    fn eq(&self, other: &Watcher) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}

impl std::cmp::Eq for Watcher {}

impl std::hash::Hash for Watcher {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        (&*self.0 as *const WatchInner).hash(state)
    }
}

impl std::ops::Deref for Watcher {
    type Target = WatchInner;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<|endoftext|>


<commit_before>use config;
use env::Env;
use library;
use nameutil::signal_to_snake;
use parser::is_empty_c_type;
use super::bounds::{Bounds, BoundType};
use super::conversion_type::ConversionType;
use super::ffi_type::used_ffi_type;
use super::parameter;
use super::ref_mode::RefMode;
use super::rust_type::{bounds_rust_type, rust_type, used_rust_type};
use traits::IntoString;
use version::Version;

#[derive(Debug)]
pub struct Trampoline {
    pub name: String,
    pub parameters: Vec<parameter::Parameter>,
    pub ret: library::Parameter,
    pub bounds: Bounds,
    pub version: Option<Version>,
    pub inhibit: bool,
}

pub type Trampolines = Vec<Trampoline>;

pub fn analyze(env: &Env, signal: &library::Signal, type_tid: library::TypeId, in_trait: bool,
               configured_signals: &[&config::signals::Signal],
               trampolines: &mut Trampolines, used_types: &mut Vec<String>,
               version: Option<Version>) -> Result<String, Vec<String>> {
    let errors = closure_errors(env, signal);
    if !errors.is_empty() {
        warn!("Can't generate {} trampoline for signal '{}'", type_tid.full_name(&env.library),
              signal.name);
        return Err(errors);
    }

    let name = format!("{}_trampoline", signal_to_snake(&signal.name));

    let owner = env.type_(type_tid);

    let c_type = format!("{}*", owner.get_glib_name().unwrap());

    //Fake
    let configured_functions: Vec<&config::functions::Function> = Vec::new();

    //TODO: move to object.signal.return config
    let inhibit = configured_signals.iter().any(|f| f.inhibit);
    if inhibit {
        if signal.ret.typ != library::TypeId::tid_bool() {
            error!("Wrong return type for Inhibit for signal '{}'", signal.name);
        }
        used_types.push("::signal::Inhibit".into());
    }

    let mut bounds: Bounds = Default::default();

    let mut parameters: Vec<parameter::Parameter> = Vec::with_capacity(signal.parameters.len() + 1);

    if let Some(s) = used_ffi_type(env, type_tid, &c_type) {
        used_types.push(s);
    }

    let this = parameter::Parameter {
        name: "this".to_owned(),
        typ: type_tid,
        c_type: c_type,
        instance_parameter: false, //true,
        direction: library::ParameterDirection::In,
        transfer: library::Transfer::None,
        caller_allocates: false,
        nullable: library::Nullable(false),
        allow_none: false,
        is_error: false,
        ref_mode: RefMode::ByRef,
        to_glib_extra: String::new(),
    };
    parameters.push(this);

    if in_trait {
        let type_name = bounds_rust_type(env, type_tid);
        bounds.add_parameter("this", &type_name.into_string(), BoundType::IsA);
    }

    for par in &signal.parameters {
        let analysis = parameter::analyze(env, par, &configured_functions);

        if let Ok(s) = used_rust_type(env, par.typ) {
            used_types.push(s);
        }
        if let Some(s) = used_ffi_type(env, par.typ, &par.c_type) {
            used_types.push(s);
        }

        parameters.push(analysis);
    }

    let mut ret_nullable = signal.ret.nullable;

    if signal.ret.typ != Default::default() {
        if let Ok(s) = used_rust_type(env, signal.ret.typ) {
            used_types.push(s);
        }
        if let Some(s) = used_ffi_type(env, signal.ret.typ, &signal.ret.c_type) {
            used_types.push(s);
        }

        let nullable_override = configured_signals.iter()
            .filter_map(|f| f.ret.nullable)
            .next();
        if let Some(val) = nullable_override {
            ret_nullable = val;
        }
    }

    let ret = library::Parameter {
        nullable: ret_nullable,
        .. signal.ret.clone()
    };

    let trampoline = Trampoline {
        name: name.clone(),
        parameters: parameters,
        ret: ret,
        bounds: bounds,
        version: version,
        inhibit: inhibit,
    };
    trampolines.push(trampoline);
    Ok(name)
}

fn closure_errors(env: &Env, signal: &library::Signal) -> Vec<String> {
    let mut errors: Vec<String> = Vec::new();
    for par in &signal.parameters {
        if let Some(error) = type_error(env, par) {
            errors.push(format!("{} {}: {}", error, par.name,
                                par.typ.full_name(&env.library)));
        }
    }
    if signal.ret.typ != Default::default() {
        if let Some(error) = type_error(env, &signal.ret) {
            errors.push(format!("{} return value {}", error,
                                signal.ret.typ.full_name(&env.library)));
        }
    }
    errors
}

fn type_error(env: &Env, par: &library::Parameter) -> Option<&'static str> {
    use super::rust_type::TypeError::*;
    if par.direction == library::ParameterDirection::Out {
        Some("Out")
    } else if par.direction == library::ParameterDirection::InOut {
        Some("InOut")
    } else if is_empty_c_type(&par.c_type) {
        Some("Empty ctype")
    } else if ConversionType::of(&env.library, par.typ) == ConversionType::Unknown {
        Some("Unknown conversion")
    } else {
        match rust_type(env, par.typ) {
            Err(Ignored(_)) => Some("Ignored"),
            Err(Mismatch(_)) => Some("Mismatch"),
            Err(Unimplemented(_)) => Some("Unimplemented"),
            Ok(_) => None,
        }
    }
}
<commit_msg>Rename variable<commit_after>use config;
use env::Env;
use library;
use nameutil::signal_to_snake;
use parser::is_empty_c_type;
use super::bounds::{Bounds, BoundType};
use super::conversion_type::ConversionType;
use super::ffi_type::used_ffi_type;
use super::parameter;
use super::ref_mode::RefMode;
use super::rust_type::{bounds_rust_type, rust_type, used_rust_type};
use traits::IntoString;
use version::Version;

#[derive(Debug)]
pub struct Trampoline {
    pub name: String,
    pub parameters: Vec<parameter::Parameter>,
    pub ret: library::Parameter,
    pub bounds: Bounds,
    pub version: Option<Version>,
    pub inhibit: bool,
}

pub type Trampolines = Vec<Trampoline>;

pub fn analyze(env: &Env, signal: &library::Signal, type_tid: library::TypeId, in_trait: bool,
               configured_signals: &[&config::signals::Signal],
               trampolines: &mut Trampolines, used_types: &mut Vec<String>,
               version: Option<Version>) -> Result<String, Vec<String>> {
    let errors = closure_errors(env, signal);
    if !errors.is_empty() {
        warn!("Can't generate {} trampoline for signal '{}'", type_tid.full_name(&env.library),
              signal.name);
        return Err(errors);
    }

    let name = format!("{}_trampoline", signal_to_snake(&signal.name));

    let owner = env.type_(type_tid);

    let c_type = format!("{}*", owner.get_glib_name().unwrap());

    //Fake
    let configured_functions: Vec<&config::functions::Function> = Vec::new();

    //TODO: move to object.signal.return config
    let inhibit = configured_signals.iter().any(|f| f.inhibit);
    if inhibit {
        if signal.ret.typ != library::TypeId::tid_bool() {
            error!("Wrong return type for Inhibit for signal '{}'", signal.name);
        }
        used_types.push("::signal::Inhibit".into());
    }

    let mut bounds: Bounds = Default::default();

    let mut parameters: Vec<parameter::Parameter> = Vec::with_capacity(signal.parameters.len() + 1);

    if let Some(s) = used_ffi_type(env, type_tid, &c_type) {
        used_types.push(s);
    }

    let this = parameter::Parameter {
        name: "this".to_owned(),
        typ: type_tid,
        c_type: c_type,
        instance_parameter: false, //true,
        direction: library::ParameterDirection::In,
        transfer: library::Transfer::None,
        caller_allocates: false,
        nullable: library::Nullable(false),
        allow_none: false,
        is_error: false,
        ref_mode: RefMode::ByRef,
        to_glib_extra: String::new(),
    };
    parameters.push(this);

    if in_trait {
        let type_name = bounds_rust_type(env, type_tid);
        bounds.add_parameter("this", &type_name.into_string(), BoundType::IsA);
    }

    for par in &signal.parameters {
        let analysis = parameter::analyze(env, par, &configured_functions);

        if let Ok(s) = used_rust_type(env, par.typ) {
            used_types.push(s);
        }
        if let Some(s) = used_ffi_type(env, par.typ, &par.c_type) {
            used_types.push(s);
        }

        parameters.push(analysis);
    }

    let mut ret_nullable = signal.ret.nullable;

    if signal.ret.typ != Default::default() {
        if let Ok(s) = used_rust_type(env, signal.ret.typ) {
            used_types.push(s);
        }
        if let Some(s) = used_ffi_type(env, signal.ret.typ, &signal.ret.c_type) {
            used_types.push(s);
        }

        let nullable_override = configured_signals.iter()
            .filter_map(|f| f.ret.nullable)
            .next();
        if let Some(nullable) = nullable_override {
            ret_nullable = nullable;
        }
    }

    let ret = library::Parameter {
        nullable: ret_nullable,
        .. signal.ret.clone()
    };

    let trampoline = Trampoline {
        name: name.clone(),
        parameters: parameters,
        ret: ret,
        bounds: bounds,
        version: version,
        inhibit: inhibit,
    };
    trampolines.push(trampoline);
    Ok(name)
}

fn closure_errors(env: &Env, signal: &library::Signal) -> Vec<String> {
    let mut errors: Vec<String> = Vec::new();
    for par in &signal.parameters {
        if let Some(error) = type_error(env, par) {
            errors.push(format!("{} {}: {}", error, par.name,
                                par.typ.full_name(&env.library)));
        }
    }
    if signal.ret.typ != Default::default() {
        if let Some(error) = type_error(env, &signal.ret) {
            errors.push(format!("{} return value {}", error,
                                signal.ret.typ.full_name(&env.library)));
        }
    }
    errors
}

fn type_error(env: &Env, par: &library::Parameter) -> Option<&'static str> {
    use super::rust_type::TypeError::*;
    if par.direction == library::ParameterDirection::Out {
        Some("Out")
    } else if par.direction == library::ParameterDirection::InOut {
        Some("InOut")
    } else if is_empty_c_type(&par.c_type) {
        Some("Empty ctype")
    } else if ConversionType::of(&env.library, par.typ) == ConversionType::Unknown {
        Some("Unknown conversion")
    } else {
        match rust_type(env, par.typ) {
            Err(Ignored(_)) => Some("Ignored"),
            Err(Mismatch(_)) => Some("Mismatch"),
            Err(Unimplemented(_)) => Some("Unimplemented"),
            Ok(_) => None,
        }
    }
}
<|endoftext|>


<commit_before>use luhn::*;

fn process_valid_case(number: &str, is_luhn_expected: bool) {
    assert_eq!(is_valid(number), is_luhn_expected);
}

#[test]
fn test_single_digit_strings_can_not_be_valid() {
    process_valid_case("1", false);
}

#[test]
#[ignore]
fn test_a_single_zero_is_invalid() {
    process_valid_case("0", false);
}

#[test]
#[ignore]
fn test_a_simple_valid_sin_that_remains_valid_if_reversed() {
    process_valid_case("059", true);
}

#[test]
#[ignore]
fn test_a_simple_valid_sin_that_becomes_invalid_if_reversed() {
    process_valid_case("59", true);
}

#[test]
#[ignore]
fn test_a_valid_canadian_sin() {
    process_valid_case("055 444 285", true);
}

#[test]
#[ignore]
fn test_invalid_canadian_sin() {
    process_valid_case("055 444 286", false);
}

#[test]
#[ignore]
fn test_invalid_credit_card() {
    process_valid_case("8273 1232 7352 0569", false);
}

#[test]
#[ignore]
fn test_valid_number_with_an_even_number_of_digits() {
    process_valid_case("095 245 88", true);
}

#[test]
#[ignore]
fn strings_that_contain_non_digits_are_invalid() {
    process_valid_case("055a 444 285", false);
}

#[test]
#[ignore]
fn test_valid_strings_with_punctuation_included_become_invalid() {
    process_valid_case("055-444-285", false);
}

#[test]
#[ignore]
fn symbols_are_invalid() {
    process_valid_case("055£ 444$ 285", false);
}

#[test]
#[ignore]
fn test_single_zero_with_space_is_invalid() {
    process_valid_case(" 0", false);
}

#[test]
#[ignore]
fn test_more_than_a_single_zero_is_valid() {
    process_valid_case("0000 0", true);
}

#[test]
#[ignore]
fn test_input_digit_9_is_correctly_converted_to_output_digit_9() {
    process_valid_case("091", true);
}

#[test]
#[ignore]
/// using ascii value for doubled non-digit isn't allowed
/// Convert non-digits to their ascii values and then offset them by 48 sometimes accidentally declare an invalid string to be valid.
/// This test is designed to avoid that solution.
fn test_using_ascii_value_for_doubled_nondigit_isnt_allowed() {
    process_valid_case(":9", false);
}

#[test]
#[ignore]
/// valid strings with a non-digit added at the end become invalid
fn test_valid_strings_with_a_nondigit_added_at_the_end_become_invalid() {
    process_valid_case("059a", false);
}

#[test]
#[ignore]
/// valid strings with symbols included become invalid
fn test_valid_strings_with_symbols_included_become_invalid() {
    process_valid_case("055# 444$ 285", false);
}

#[test]
#[ignore]
/// using ascii value for non-doubled non-digit isn't allowed
/// Convert non-digits to their ascii values and then offset them by 48 sometimes accidentally declare an invalid string to be valid.
/// This test is designed to avoid that solution.
fn test_using_ascii_value_for_nondoubled_nondigit_isnt_allowed() {
    process_valid_case("055b 444 285", false);
}

#[test]
#[ignore]
/// valid number with an odd number of spaces
fn test_valid_number_with_an_odd_number_of_spaces() {
    process_valid_case("234 567 891 234", true);
}
<commit_msg>Update luhn.rs<commit_after>use luhn::*;

fn process_valid_case(number: &str, is_luhn_expected: bool) {
    assert_eq!(is_valid(number), is_luhn_expected);
}

#[test]
fn test_single_digit_strings_can_not_be_valid() {
    process_valid_case("1", false);
}

#[test]
#[ignore]
fn test_a_single_zero_is_invalid() {
    process_valid_case("0", false);
}

#[test]
#[ignore]
fn test_a_simple_valid_sin_that_remains_valid_if_reversed() {
    process_valid_case("059", true);
}

#[test]
#[ignore]
fn test_a_simple_valid_sin_that_becomes_invalid_if_reversed() {
    process_valid_case("59", true);
}

#[test]
#[ignore]
fn test_a_valid_canadian_sin() {
    process_valid_case("055 444 285", true);
}

#[test]
#[ignore]
fn test_invalid_canadian_sin() {
    process_valid_case("055 444 286", false);
}

#[test]
#[ignore]
fn test_invalid_credit_card() {
    process_valid_case("8273 1232 7352 0569", false);
}

#[test]
#[ignore]
fn test_valid_number_with_an_even_number_of_digits() {
    process_valid_case("095 245 88", true);
}

#[test]
#[ignore]
fn strings_that_contain_non_digits_are_invalid() {
    process_valid_case("055a 444 285", false);
}

#[test]
#[ignore]
fn test_valid_strings_with_punctuation_included_become_invalid() {
    process_valid_case("055-444-285", false);
}

#[test]
#[ignore]
fn symbols_are_invalid() {
    process_valid_case("055£ 444$ 285", false);
}

#[test]
#[ignore]
fn test_single_zero_with_space_is_invalid() {
    process_valid_case(" 0", false);
}

#[test]
#[ignore]
fn test_more_than_a_single_zero_is_valid() {
    process_valid_case("0000 0", true);
}

#[test]
#[ignore]
fn test_input_digit_9_is_correctly_converted_to_output_digit_9() {
    process_valid_case("091", true);
}

#[test]
#[ignore]
/// using ascii value for doubled non-digit isn't allowed
/// Convert non-digits to their ascii values and then offset them by 48 sometimes accidentally declare an invalid string to be valid.
/// This test is designed to avoid that solution.
fn test_using_ascii_value_for_doubled_nondigit_isnt_allowed() {
    process_valid_case(":9", false);
}

#[test]
#[ignore]
/// valid strings with a non-digit added at the end become invalid
fn test_valid_strings_with_a_nondigit_added_at_the_end_become_invalid() {
    process_valid_case("059a", false);
}

#[test]
#[ignore]
/// valid strings with symbols included become invalid
fn test_valid_strings_with_symbols_included_become_invalid() {
    process_valid_case("055# 444$ 285", false);
}

#[test]
#[ignore]
/// using ascii value for non-doubled non-digit isn't allowed
/// Convert non-digits to their ascii values and then offset them by 48 sometimes accidentally declare an invalid string to be valid.
/// This test is designed to avoid that solution.
fn test_using_ascii_value_for_nondoubled_nondigit_isnt_allowed() {
    process_valid_case("055b 444 285", false);
}

#[test]
#[ignore]
/// valid number with an odd number of spaces
fn test_valid_number_with_an_odd_number_of_spaces() {
    process_valid_case("234 567 891 234", true);
}

#[test]
#[ignore]
/// non-numeric, non-space char in the middle with a sum that's divisible by 10 isn't allowed
fn test_invalid_char_in_middle_with_sum_divisible_by_10_isnt_allowed() {
    process_valid_case("59%59", false);
}
<|endoftext|>


<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::process::Command;
use std::env;

fn main() {
    let root = env::var("CARGO_MANIFEST_DIR").unwrap();

    Command::new("make").current_dir("htslib")
                        .arg("CFLAGS=-g -Wall -O2 -fPIC")
                        .arg("lib-static")
                        .status().ok().expect("Failed to build htslib");

    println!("cargo:rustc-flags=-L {}/htslib -l static=hts -l z", root);
}
<commit_msg>Try to set htslib dir relative to cargo path.<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

use std::process::Command;
use std::env;

fn main() {
    let root = env::var("CARGO_MANIFEST_DIR").unwrap();

    Command::new("make").current_dir(format!("{}/htslib", root))
                        .arg("CFLAGS=-g -Wall -O2 -fPIC")
                        .arg("lib-static")
                        .status().ok().expect("Failed to build htslib");

    println!("cargo:rustc-flags=-L {}/htslib -l static=hts -l z", root);
}
<|endoftext|>


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

use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

const MIN_VERSION: &'static str = "7.0";
const MAX_VERSION: &'static str = "7.1";

static HEADER: &'static str = "#include <MagickWand/MagickWand.h>\n";

fn main() {
    if cfg!(target_os = "freebsd") {
        // pkg_config does not seem to work properly on FreeBSD, so
        // hard-code the builder settings for the time being.
        env_var_set_default(
            "IMAGE_MAGICK_INCLUDE_DIRS",
            "/usr/local/include/ImageMagick-7",
        );
        // Need to hack the linker flags as well.
        env_var_set_default("IMAGE_MAGICK_LIB_DIRS", "/usr/local/lib");
        env_var_set_default("IMAGE_MAGICK_LIBS", "MagickWand-7");
    }

    let lib_dirs = find_image_magick_lib_dirs();
    for d in &lib_dirs {
        if !d.exists() {
            panic!(
                "ImageMagick library directory does not exist: {}",
                d.to_string_lossy()
            );
        }
        println!("cargo:rustc-link-search=native={}", d.to_string_lossy());
    }
    let include_dirs = find_image_magick_include_dirs();
    for d in &include_dirs {
        if !d.exists() {
            panic!(
                "ImageMagick include directory does not exist: {}",
                d.to_string_lossy()
            );
        }
        println!("cargo:include={}", d.to_string_lossy());
    }
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_LIBS");

    let target = env::var("TARGET").unwrap();
    let libs_env = env::var("IMAGE_MAGICK_LIBS").ok();
    let libs = match libs_env {
        Some(ref v) => v.split(":").map(|x| x.to_owned()).collect(),
        None => {
            if target.contains("windows") {
                vec!["CORE_RL_MagickWand_".to_string()]
            } else if target.contains("freebsd") {
                vec!["MagickWand-7".to_string()]
            } else {
                run_pkg_config().libs
            }
        }
    };

    let kind = determine_mode(&lib_dirs, libs.as_slice());
    for lib in libs.into_iter() {
        println!("cargo:rustc-link-lib={}={}", kind, lib);
    }

    // If the generated bindings are missing, generate them now.
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let bindings_path_str = out_dir.join("bindings.rs");

    #[derive(Debug)]
    struct IgnoreMacros(HashSet<String>);

    impl bindgen::callbacks::ParseCallbacks for IgnoreMacros {
        fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior {
            if self.0.contains(name) {
                bindgen::callbacks::MacroParsingBehavior::Ignore
            } else {
                bindgen::callbacks::MacroParsingBehavior::Default
            }
        }
    }

    let ignored_macros = IgnoreMacros(
        vec![
            "FP_INFINITE".into(),
            "FP_NAN".into(),
            "FP_NORMAL".into(),
            "FP_SUBNORMAL".into(),
            "FP_ZERO".into(),
            "IPPORT_RESERVED".into(),
            "FP_INT_UPWARD".into(),
            "FP_INT_DOWNWARD".into(),
            "FP_INT_TOWARDZERO".into(),
            "FP_INT_TONEARESTFROMZERO".into(),
            "FP_INT_TONEAREST".into(),
        ]
        .into_iter()
        .collect(),
    );

    if !Path::new(&bindings_path_str).exists() {
        // Create the header file that rust-bindgen needs as input.
        let gen_h_path = out_dir.join("gen.h");
        let mut gen_h = File::create(&gen_h_path).expect("could not create file");
        gen_h
            .write_all(HEADER.as_bytes())
            .expect("could not write header file");

        // Geneate the bindings.
        let mut builder = bindgen::Builder::default()
            .emit_builtins()
            .ctypes_prefix("libc")
            .raw_line("extern crate libc;")
            .header(gen_h_path.to_str().unwrap())
            .size_t_is_usize(true)
            .parse_callbacks(Box::new(ignored_macros))
            .blacklist_type("timex")
            .blacklist_function("clock_adjtime");

        for d in include_dirs {
            builder = builder.clang_arg(format!("-I{}", d.to_string_lossy()));
        }

        let bindings = builder.generate().unwrap();
        let mut file = File::create(&bindings_path_str).expect("could not create bindings file");
        // Work around the include! issue in rustc (as described in the
        // rust-bindgen README file) by wrapping the generated code in a
        // `pub mod` declaration; see issue #359 in (old) rust-bindgen.
        file.write(b"pub mod bindings {\n").unwrap();
        file.write(bindings.to_string().as_bytes()).unwrap();
        file.write(b"\n}").unwrap();

        std::fs::remove_file(&gen_h_path).expect("could not remove header file");
    }
}

fn env_var_set_default(name: &str, value: &str) {
    if env::var(name).is_err() {
        env::set_var(name, value);
    }
}

fn find_image_magick_lib_dirs() -> Vec<PathBuf> {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_LIB_DIRS");
    env::var("IMAGE_MAGICK_LIB_DIRS")
        .map(|x| x.split(":").map(PathBuf::from).collect::<Vec<PathBuf>>())
        .or_else(|_| Ok(vec![find_image_magick_dir()?.join("lib")]))
        .or_else(|_: env::VarError| -> Result<_, env::VarError> { Ok(run_pkg_config().link_paths) })
        .expect("Couldn't find ImageMagick library directory")
}

fn find_image_magick_include_dirs() -> Vec<PathBuf> {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_INCLUDE_DIRS");
    env::var("IMAGE_MAGICK_INCLUDE_DIRS")
        .map(|x| x.split(":").map(PathBuf::from).collect::<Vec<PathBuf>>())
        .or_else(|_| Ok(vec![find_image_magick_dir()?.join("include")]))
        .or_else(|_: env::VarError| -> Result<_, env::VarError> {
            Ok(run_pkg_config().include_paths)
        })
        .expect("Couldn't find ImageMagick include directory")
}

fn find_image_magick_dir() -> Result<PathBuf, env::VarError> {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_DIR");
    env::var("IMAGE_MAGICK_DIR").map(PathBuf::from)
}

fn determine_mode<T: AsRef<str>>(libdirs: &Vec<PathBuf>, libs: &[T]) -> &'static str {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_STATIC");
    let kind = env::var("IMAGE_MAGICK_STATIC").ok();
    match kind.as_ref().map(|s| &s[..]) {
        Some("0") => return "dylib",
        Some(_) => return "static",
        None => {}
    }

    // See what files we actually have to link against, and see what our
    // possibilities even are.
    let files = libdirs
        .into_iter()
        .flat_map(|d| d.read_dir().unwrap())
        .map(|e| e.unwrap())
        .map(|e| e.file_name())
        .filter_map(|e| e.into_string().ok())
        .collect::<HashSet<_>>();
    let can_static = libs.iter().all(|l| {
        files.contains(&format!("lib{}.a", l.as_ref()))
            || files.contains(&format!("{}.lib", l.as_ref()))
    });
    let can_dylib = libs.iter().all(|l| {
        files.contains(&format!("lib{}.so", l.as_ref()))
            || files.contains(&format!("{}.dll", l.as_ref()))
            || files.contains(&format!("lib{}.dylib", l.as_ref()))
    });

    match (can_static, can_dylib) {
        (true, false) => return "static",
        (false, true) => return "dylib",
        (false, false) => {
            panic!(
                "ImageMagick libdirs at `{:?}` do not contain the required files \
                 to either statically or dynamically link ImageMagick",
                libdirs
            );
        }
        (true, true) => {}
    }

    // default
    "dylib"
}

fn run_pkg_config() -> pkg_config::Library {
    // Assert that the appropriate version of MagickWand is installed,
    // since we are very dependent on the particulars of MagickWand.
    pkg_config::Config::new()
        .cargo_metadata(false)
        .atleast_version(MIN_VERSION)
        .probe("MagickWand")
        .unwrap();
    // Check the maximum version separately as pkg-config will ignore that
    // option when combined with (certain) other options. And since the
    // pkg-config crate always adds those other flags, we must run the
    // command directly.
    if !Command::new("pkg-config")
        .arg(format!("--max-version={}", MAX_VERSION))
        .arg("MagickWand")
        .status()
        .unwrap()
        .success()
    {
        panic!(format!(
            "MagickWand version must be no higher than {}",
            MAX_VERSION
        ));
    }
    // We have to split the version check and the cflags/libs check because
    // you can't do both at the same time on RHEL (apparently).
    pkg_config::Config::new()
        .cargo_metadata(false)
        .probe("MagickWand")
        .unwrap()
}
<commit_msg>Add quantum depth<commit_after>/*
 * Copyright 2016-2018 Nathan Fiedler
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
extern crate bindgen;
extern crate pkg_config;

use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

const MIN_VERSION: &'static str = "7.0";
const MAX_VERSION: &'static str = "7.1";

static HEADER: &'static str = r#"#define MAGICKCORE_QUANTUM_DEPTH 8
#include <MagickWand/MagickWand.h>
"#;

fn main() {
    if cfg!(target_os = "freebsd") {
        // pkg_config does not seem to work properly on FreeBSD, so
        // hard-code the builder settings for the time being.
        env_var_set_default(
            "IMAGE_MAGICK_INCLUDE_DIRS",
            "/usr/local/include/ImageMagick-7",
        );
        // Need to hack the linker flags as well.
        env_var_set_default("IMAGE_MAGICK_LIB_DIRS", "/usr/local/lib");
        env_var_set_default("IMAGE_MAGICK_LIBS", "MagickWand-7");
    }

    let lib_dirs = find_image_magick_lib_dirs();
    for d in &lib_dirs {
        if !d.exists() {
            panic!(
                "ImageMagick library directory does not exist: {}",
                d.to_string_lossy()
            );
        }
        println!("cargo:rustc-link-search=native={}", d.to_string_lossy());
    }
    let include_dirs = find_image_magick_include_dirs();
    for d in &include_dirs {
        if !d.exists() {
            panic!(
                "ImageMagick include directory does not exist: {}",
                d.to_string_lossy()
            );
        }
        println!("cargo:include={}", d.to_string_lossy());
    }
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_LIBS");

    let target = env::var("TARGET").unwrap();
    let libs_env = env::var("IMAGE_MAGICK_LIBS").ok();
    let libs = match libs_env {
        Some(ref v) => v.split(":").map(|x| x.to_owned()).collect(),
        None => {
            if target.contains("windows") {
                vec!["CORE_RL_MagickWand_".to_string()]
            } else if target.contains("freebsd") {
                vec!["MagickWand-7".to_string()]
            } else {
                run_pkg_config().libs
            }
        }
    };

    let kind = determine_mode(&lib_dirs, libs.as_slice());
    for lib in libs.into_iter() {
        println!("cargo:rustc-link-lib={}={}", kind, lib);
    }

    // If the generated bindings are missing, generate them now.
    let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
    let bindings_path_str = out_dir.join("bindings.rs");

    #[derive(Debug)]
    struct IgnoreMacros(HashSet<String>);

    impl bindgen::callbacks::ParseCallbacks for IgnoreMacros {
        fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior {
            if self.0.contains(name) {
                bindgen::callbacks::MacroParsingBehavior::Ignore
            } else {
                bindgen::callbacks::MacroParsingBehavior::Default
            }
        }
    }

    let ignored_macros = IgnoreMacros(
        vec![
            "FP_INFINITE".into(),
            "FP_NAN".into(),
            "FP_NORMAL".into(),
            "FP_SUBNORMAL".into(),
            "FP_ZERO".into(),
            "IPPORT_RESERVED".into(),
            "FP_INT_UPWARD".into(),
            "FP_INT_DOWNWARD".into(),
            "FP_INT_TOWARDZERO".into(),
            "FP_INT_TONEARESTFROMZERO".into(),
            "FP_INT_TONEAREST".into(),
        ]
        .into_iter()
        .collect(),
    );

    if !Path::new(&bindings_path_str).exists() {
        // Create the header file that rust-bindgen needs as input.
        let gen_h_path = out_dir.join("gen.h");
        let mut gen_h = File::create(&gen_h_path).expect("could not create file");
        gen_h
            .write_all(HEADER.as_bytes())
            .expect("could not write header file");

        // Geneate the bindings.
        let mut builder = bindgen::Builder::default()
            .emit_builtins()
            .ctypes_prefix("libc")
            .raw_line("extern crate libc;")
            .header(gen_h_path.to_str().unwrap())
            .size_t_is_usize(true)
            .parse_callbacks(Box::new(ignored_macros))
            .blacklist_type("timex")
            .blacklist_function("clock_adjtime");

        for d in include_dirs {
            builder = builder.clang_arg(format!("-I{}", d.to_string_lossy()));
        }

        let bindings = builder.generate().unwrap();
        let mut file = File::create(&bindings_path_str).expect("could not create bindings file");
        // Work around the include! issue in rustc (as described in the
        // rust-bindgen README file) by wrapping the generated code in a
        // `pub mod` declaration; see issue #359 in (old) rust-bindgen.
        file.write(b"pub mod bindings {\n").unwrap();
        file.write(bindings.to_string().as_bytes()).unwrap();
        file.write(b"\n}").unwrap();

        std::fs::remove_file(&gen_h_path).expect("could not remove header file");
    }
}

fn env_var_set_default(name: &str, value: &str) {
    if env::var(name).is_err() {
        env::set_var(name, value);
    }
}

fn find_image_magick_lib_dirs() -> Vec<PathBuf> {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_LIB_DIRS");
    env::var("IMAGE_MAGICK_LIB_DIRS")
        .map(|x| x.split(":").map(PathBuf::from).collect::<Vec<PathBuf>>())
        .or_else(|_| Ok(vec![find_image_magick_dir()?.join("lib")]))
        .or_else(|_: env::VarError| -> Result<_, env::VarError> { Ok(run_pkg_config().link_paths) })
        .expect("Couldn't find ImageMagick library directory")
}

fn find_image_magick_include_dirs() -> Vec<PathBuf> {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_INCLUDE_DIRS");
    env::var("IMAGE_MAGICK_INCLUDE_DIRS")
        .map(|x| x.split(":").map(PathBuf::from).collect::<Vec<PathBuf>>())
        .or_else(|_| Ok(vec![find_image_magick_dir()?.join("include")]))
        .or_else(|_: env::VarError| -> Result<_, env::VarError> {
            Ok(run_pkg_config().include_paths)
        })
        .expect("Couldn't find ImageMagick include directory")
}

fn find_image_magick_dir() -> Result<PathBuf, env::VarError> {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_DIR");
    env::var("IMAGE_MAGICK_DIR").map(PathBuf::from)
}

fn determine_mode<T: AsRef<str>>(libdirs: &Vec<PathBuf>, libs: &[T]) -> &'static str {
    println!("cargo:rerun-if-env-changed=IMAGE_MAGICK_STATIC");
    let kind = env::var("IMAGE_MAGICK_STATIC").ok();
    match kind.as_ref().map(|s| &s[..]) {
        Some("0") => return "dylib",
        Some(_) => return "static",
        None => {}
    }

    // See what files we actually have to link against, and see what our
    // possibilities even are.
    let files = libdirs
        .into_iter()
        .flat_map(|d| d.read_dir().unwrap())
        .map(|e| e.unwrap())
        .map(|e| e.file_name())
        .filter_map(|e| e.into_string().ok())
        .collect::<HashSet<_>>();
    let can_static = libs.iter().all(|l| {
        files.contains(&format!("lib{}.a", l.as_ref()))
            || files.contains(&format!("{}.lib", l.as_ref()))
    });
    let can_dylib = libs.iter().all(|l| {
        files.contains(&format!("lib{}.so", l.as_ref()))
            || files.contains(&format!("{}.dll", l.as_ref()))
            || files.contains(&format!("lib{}.dylib", l.as_ref()))
    });

    match (can_static, can_dylib) {
        (true, false) => return "static",
        (false, true) => return "dylib",
        (false, false) => {
            panic!(
                "ImageMagick libdirs at `{:?}` do not contain the required files \
                 to either statically or dynamically link ImageMagick",
                libdirs
            );
        }
        (true, true) => {}
    }

    // default
    "dylib"
}

fn run_pkg_config() -> pkg_config::Library {
    // Assert that the appropriate version of MagickWand is installed,
    // since we are very dependent on the particulars of MagickWand.
    pkg_config::Config::new()
        .cargo_metadata(false)
        .atleast_version(MIN_VERSION)
        .probe("MagickWand")
        .unwrap();
    // Check the maximum version separately as pkg-config will ignore that
    // option when combined with (certain) other options. And since the
    // pkg-config crate always adds those other flags, we must run the
    // command directly.
    if !Command::new("pkg-config")
        .arg(format!("--max-version={}", MAX_VERSION))
        .arg("MagickWand")
        .status()
        .unwrap()
        .success()
    {
        panic!(format!(
            "MagickWand version must be no higher than {}",
            MAX_VERSION
        ));
    }
    // We have to split the version check and the cflags/libs check because
    // you can't do both at the same time on RHEL (apparently).
    pkg_config::Config::new()
        .cargo_metadata(false)
        .probe("MagickWand")
        .unwrap()
}
<|endoftext|>


<commit_before>fn main() {
    use std::path::Path;
    use std::process::Command;
    use std::str::from_utf8;
    use std::io::Write;
    use std::fs::{File, create_dir_all};
    let mut out = File::create("build.log").unwrap();
    let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("bobyqa-cpp").join("build");
    create_dir_all(&dir).unwrap();
    let date = Command::new("date").output().unwrap();
    write!(&mut out, "# {}", from_utf8(&date.stdout[..]).unwrap()).unwrap();
    writeln!(&mut out, "# cmake").unwrap();
    let cmake = Command::new("cmake")
        .arg("-DCMAKE_VERBOSE_MAKEFILE=1").arg("-DCMAKE_BUILD_TYPE=Release").arg("..")
        .current_dir(&dir).output().unwrap();
    write!(&mut out, "{}", from_utf8(&cmake.stdout[..]).unwrap()).unwrap();
    write!(&mut out, "{}", from_utf8(&cmake.stderr[..]).unwrap()).unwrap();
    writeln!(&mut out, "{}", cmake.status).unwrap();
    assert!(cmake.status.success());
    writeln!(&mut out, "# make").unwrap();
    let make = Command::new("make").current_dir(&dir).output().unwrap();
    write!(&mut out, "{}", from_utf8(&make.stdout[..]).unwrap()).unwrap();
    write!(&mut out, "{}", from_utf8(&make.stderr[..]).unwrap()).unwrap();
    writeln!(&mut out, "{}", make.status).unwrap();
    assert!(make.status.success());
    println!("cargo:rustc-link-search=native={}", dir.join("lib").display());
    println!("cargo:rustc-link-lib=static=bobyqa");
}
<commit_msg>Show path to build log on fail<commit_after>use std::fs::File;

fn build(out: &mut File) -> bool {
    use std::path::Path;
    use std::process::Command;
    use std::str::from_utf8;
    use std::io::Write;
    use std::fs::create_dir_all;
    let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("bobyqa-cpp").join("build");
    create_dir_all(&dir).unwrap();
    let date = Command::new("date").output().unwrap();
    write!(out, "# {}", from_utf8(&date.stdout[..]).unwrap()).unwrap();
    writeln!(out, "# cmake").unwrap();
    let cmake = Command::new("cmake")
        .arg("-DCMAKE_VERBOSE_MAKEFILE=1").arg("-DCMAKE_BUILD_TYPE=Release").arg("..")
        .current_dir(&dir).output().unwrap();
    write!(out, "{}", from_utf8(&cmake.stdout[..]).unwrap()).unwrap();
    write!(out, "{}", from_utf8(&cmake.stderr[..]).unwrap()).unwrap();
    writeln!(out, "{}", cmake.status).unwrap();
    if !cmake.status.success() {
        return false;
    }
    writeln!(out, "# make").unwrap();
    let make = Command::new("make").current_dir(&dir).output().unwrap();
    write!(out, "{}", from_utf8(&make.stdout[..]).unwrap()).unwrap();
    write!(out, "{}", from_utf8(&make.stderr[..]).unwrap()).unwrap();
    writeln!(out, "{}", make.status).unwrap();
    if !make.status.success() {
        return false;
    }
    println!("cargo:rustc-link-search=native={}", dir.join("lib").display());
    println!("cargo:rustc-link-lib=static=bobyqa");
    true
}

fn main() {
    use std::fs::canonicalize;
    let mut out = File::create("build.log").unwrap();
    let log = canonicalize("build.log").unwrap();
    assert!(build(&mut out), format!("build failed, see log: {:?}", log));
}
<|endoftext|>