<commit_before>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

use id3::Tag as id3tag;
use libp2p_core::PeerId;
use mp4ameta::Tag as mp4tag;
use std::io::BufReader;
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    mem,
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
    time::Duration,
};
use tree_magic;

static TOLERANCE: usize = 5;       //! distance of Levenshtein-algorithm
static ID3_CAPACITY: usize = 1024; //! capacity to read small portion of file

struct AudioInfo {
    duration: Duration,
    album: String,
    path: String,
    //computer: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

#[allow(dead_code)]
struct CommonAudioInfo<'a> {
    title: &'a str,
    artist: &'a str,
    duration: Duration,
    album: Option<&'a str>,
    track: Option<u32>,
    album_artist: Option<&'a str>,
    genre: Option<&'a str>,
    disc: Option<u32>,
    total_discs: Option<u32>,
    total_tracks: Option<u32>,
    year: Option<i32>,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    memory: u64,
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                memory: 0,
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            // some audio files have video-mimetype
            Some("audio") | Some("video") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            Some("text") | Some("application") | Some("image") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
        // open file only once
        // fixme: fix unwraps
        let file_name = cb.to_str().unwrap();
        let file = std::fs::File::open(file_name).unwrap();
        let mut file_buffer = BufReader::with_capacity(ID3_CAPACITY, file);

        // todo: use more of tree-magic info to avoid wrong first reads
        id3tag::read_from(file_buffer.get_mut())
            .and_then(|tag| {
                // write into common audio info that can be analyzed
                let id3tag = CommonAudioInfo {
                    title: tag.title().unwrap_or(""),
                    artist: tag.artist().unwrap_or(""),
                    duration: Duration::from_secs(tag.duration().unwrap_or(0) as u64),
                    album: tag.album(),
                    track: tag.track(),
                    album_artist: tag.album_artist(),
                    genre: tag.genre(),
                    disc: tag.disc(),
                    total_discs: tag.total_discs(),
                    total_tracks: tag.total_tracks(),
                    year: tag.year(),
                };
                self.analyze_tag(data.clone(), file_stats, file_name.to_string(), id3tag);
                Ok(())
            })
            .or_else(|e_id3tag| match mp4tag::read_from(file_buffer.get_mut()) {
                Ok(tag) => {
                    // track info need extra treatment in mp4ameta
                    let try_track_info = tag.track_number();
                    let (track, total_tracks) = match try_track_info {
                        Some(good_track_info) => {
                            let (track, total_tracks) = good_track_info;
                            (Some(track as u32), Some(total_tracks as u32))
                        }
                        None => (None, None),
                    };
                    // year needs extra treatment
                    let year = tag
                        .year()
                        .map_or(None, |good_string| good_string.parse::<i32>().ok());

                    let mp4tag = CommonAudioInfo {
                        title: tag.title().unwrap_or(""),
                        artist: tag.artist().unwrap_or(""),
                        duration: Duration::from_secs(tag.duration().unwrap_or(0.0) as u64),
                        album: tag.album(),
                        track,
                        album_artist: tag.album_artist(),
                        genre: tag.genre(),
                        disc: None,        // no supported
                        total_discs: None, // no supported
                        total_tracks,
                        year,
                    };
                    self.analyze_tag(data, file_stats, file_name.to_string(), mp4tag);
                    trace!("found {}", tag.artist().unwrap_or("no good artist"));
                    Ok(())
                }
                Err(e_mp4tag) => Err((e_id3tag, e_mp4tag)),
            })
            .or_else(|e| {
                let (id3tag_error, mp4ameta_error) = e;
                error!("path: {}", file_name);
                error!("id3tag  : {:?}", id3tag_error);
                error!("mp4ameta: {:?}", mp4ameta_error);
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             paths/threads        : {nr_pathes:>width$}\n\
             memory               : {mem:>width$}kb\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            mem = self.stats.memory / 1000,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }

    fn analyze_tag(
        &mut self,
        data: SArc<SMutex<Container>>,
        file_stats: &mut FilesStat,
        file_name: String,
        audio_info: CommonAudioInfo,
    ) {
        self.stats.files.analyzed += 1;
        file_stats.analyzed += 1;

        // audio book genre set is a strong indicator
        // many discs and total discs is a strong indicator
        let mut has_enough_information = true;

        // artist + song name is key for bktree
        if audio_info.artist.is_empty() && audio_info.title.is_empty() {
            has_enough_information = false;
        }

        if has_enough_information {
            let key = [audio_info.artist, audio_info.title].join(" ");

            // a) filter numbers / remove
            // b) use album name / substract it?

            // todo: bktree and levenshtein distance, use cosine similarity instead
            let ref mut locked_container = data.lock().unwrap();
            let (vec_exact_match, vec_similarities) =
                locked_container.bk_tree.find(&key, TOLERANCE);
            if !vec_similarities.is_empty() {
                trace!("close: {:?} to {:?},", &vec_similarities, &key);
            }
            // if exact match, don't insert!!
            if vec_exact_match.is_empty() {
                // todo: also decide when to not add and insert then

                let key = key.clone();
                let value = Box::new(AudioInfo {
                    duration: audio_info.duration,
                    album: audio_info.album.unwrap_or("no album").to_string(),
                    path: file_name.to_string(),
                });
                let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                locked_container.bk_tree.insert(key, value);
                self.stats.memory += mem_size as u64;
            } else {
                // exact match with certain AudioInfo
                trace!(
                    "for {:?}, {} exact matches found!",
                    &key,
                    vec_exact_match.len()
                );
                for new_audio_info in vec_exact_match {
                    let time_distance = new_audio_info.duration - audio_info.duration;
                    if time_distance > Duration::from_secs(0) {
                        trace!(
                            "same: but time differs {:?} seconds with album name old '{}' and new: '{}'!",
                            time_distance,
                            new_audio_info.album,
                            audio_info.album.unwrap_or("no album").to_string()
                        );
                    }
                }
            }
        }
    }
}
<commit_msg>[collection] before comments were not legal rust comment<commit_after>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

use id3::Tag as id3tag;
use libp2p_core::PeerId;
use mp4ameta::Tag as mp4tag;
use std::io::BufReader;
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    mem,
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
    time::Duration,
};
use tree_magic;

static TOLERANCE: usize = 5;
/// distance of Levenshtein-algorithm
static ID3_CAPACITY: usize = 1024;
/// capacity to read small portion of file

struct AudioInfo {
    duration: Duration,
    album: String,
    path: String,
    //computer: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

#[allow(dead_code)]
struct CommonAudioInfo<'a> {
    title: &'a str,
    artist: &'a str,
    duration: Duration,
    album: Option<&'a str>,
    track: Option<u32>,
    album_artist: Option<&'a str>,
    genre: Option<&'a str>,
    disc: Option<u32>,
    total_discs: Option<u32>,
    total_tracks: Option<u32>,
    year: Option<i32>,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    memory: u64,
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                memory: 0,
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            // some audio files have video-mimetype
            Some("audio") | Some("video") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            Some("text") | Some("application") | Some("image") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
        // open file only once
        // fixme: fix unwraps
        let file_name = cb.to_str().unwrap();
        let file = std::fs::File::open(file_name).unwrap();
        let mut file_buffer = BufReader::with_capacity(ID3_CAPACITY, file);

        // todo: use more of tree-magic info to avoid wrong first reads
        id3tag::read_from(file_buffer.get_mut())
            .and_then(|tag| {
                // write into common audio info that can be analyzed
                let id3tag = CommonAudioInfo {
                    title: tag.title().unwrap_or(""),
                    artist: tag.artist().unwrap_or(""),
                    duration: Duration::from_secs(tag.duration().unwrap_or(0) as u64),
                    album: tag.album(),
                    track: tag.track(),
                    album_artist: tag.album_artist(),
                    genre: tag.genre(),
                    disc: tag.disc(),
                    total_discs: tag.total_discs(),
                    total_tracks: tag.total_tracks(),
                    year: tag.year(),
                };
                self.analyze_tag(data.clone(), file_stats, file_name.to_string(), id3tag);
                Ok(())
            })
            .or_else(|e_id3tag| match mp4tag::read_from(file_buffer.get_mut()) {
                Ok(tag) => {
                    // track info need extra treatment in mp4ameta
                    let try_track_info = tag.track_number();
                    let (track, total_tracks) = match try_track_info {
                        Some(good_track_info) => {
                            let (track, total_tracks) = good_track_info;
                            (Some(track as u32), Some(total_tracks as u32))
                        }
                        None => (None, None),
                    };
                    // year needs extra treatment
                    let year = tag
                        .year()
                        .map_or(None, |good_string| good_string.parse::<i32>().ok());

                    let mp4tag = CommonAudioInfo {
                        title: tag.title().unwrap_or(""),
                        artist: tag.artist().unwrap_or(""),
                        duration: Duration::from_secs(tag.duration().unwrap_or(0.0) as u64),
                        album: tag.album(),
                        track,
                        album_artist: tag.album_artist(),
                        genre: tag.genre(),
                        disc: None,        // no supported
                        total_discs: None, // no supported
                        total_tracks,
                        year,
                    };
                    self.analyze_tag(data, file_stats, file_name.to_string(), mp4tag);
                    trace!("found {}", tag.artist().unwrap_or("no good artist"));
                    Ok(())
                }
                Err(e_mp4tag) => Err((e_id3tag, e_mp4tag)),
            })
            .or_else(|e| {
                let (id3tag_error, mp4ameta_error) = e;
                error!("path: {}", file_name);
                error!("id3tag  : {:?}", id3tag_error);
                error!("mp4ameta: {:?}", mp4ameta_error);
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             paths/threads        : {nr_pathes:>width$}\n\
             memory               : {mem:>width$}kb\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            mem = self.stats.memory / 1000,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }

    fn analyze_tag(
        &mut self,
        data: SArc<SMutex<Container>>,
        file_stats: &mut FilesStat,
        file_name: String,
        audio_info: CommonAudioInfo,
    ) {
        self.stats.files.analyzed += 1;
        file_stats.analyzed += 1;

        // audio book genre set is a strong indicator
        // many discs and total discs is a strong indicator
        let mut has_enough_information = true;

        // artist + song name is key for bktree
        if audio_info.artist.is_empty() && audio_info.title.is_empty() {
            has_enough_information = false;
        }

        if has_enough_information {
            let key = [audio_info.artist, audio_info.title].join(" ");

            // a) filter numbers / remove
            // b) use album name / substract it?

            // todo: bktree and levenshtein distance, use cosine similarity instead
            let ref mut locked_container = data.lock().unwrap();
            let (vec_exact_match, vec_similarities) =
                locked_container.bk_tree.find(&key, TOLERANCE);
            if !vec_similarities.is_empty() {
                trace!("close: {:?} to {:?},", &vec_similarities, &key);
            }
            // if exact match, don't insert!!
            if vec_exact_match.is_empty() {
                // todo: also decide when to not add and insert then

                let key = key.clone();
                let value = Box::new(AudioInfo {
                    duration: audio_info.duration,
                    album: audio_info.album.unwrap_or("no album").to_string(),
                    path: file_name.to_string(),
                });
                let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                locked_container.bk_tree.insert(key, value);
                self.stats.memory += mem_size as u64;
            } else {
                // exact match with certain AudioInfo
                trace!(
                    "for {:?}, {} exact matches found!",
                    &key,
                    vec_exact_match.len()
                );
                for new_audio_info in vec_exact_match {
                    let time_distance = new_audio_info.duration - audio_info.duration;
                    if time_distance > Duration::from_secs(0) {
                        trace!(
                            "same: but time differs {:?} seconds with album name old '{}' and new: '{}'!",
                            time_distance,
                            new_audio_info.album,
                            audio_info.album.unwrap_or("no album").to_string()
                        );
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Extended attribute support for Darwin and Linux systems.
#![allow(trivial_casts)]  // for ARM
extern crate libc;

use std::io;
use std::path::Path;

pub const ENABLED: bool = cfg!(feature="git") && cfg!(any(target_os="macos", target_os="linux"));

pub trait FileAttributes {
    fn attributes(&self) -> io::Result<Vec<Attribute>>;
    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(lister::Lister::new(FollowSymlinks::Yes), &self)
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(lister::Lister::new(FollowSymlinks::No), &self)
    }
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }
}

/// Attributes which can be passed to `Attribute::list_with_flags`
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[derive(Copy, Clone)]
pub enum FollowSymlinks {
    Yes,
    No
}

/// Extended attribute
#[derive(Debug, Clone)]
pub struct Attribute {
    pub name: String,
    pub size: usize,
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn list_attrs(lister: lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
    use std::ffi::CString;

    let c_path = match path.to_str().and_then(|s| { CString::new(s).ok() }) {
        Some(cstring) => cstring,
        None => return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?")),
    };

    let mut names = Vec::new();
    let bufsize = lister.listxattr_first(&c_path);

    if bufsize < 0 {
        return Err(io::Error::last_os_error());
    }
    else if bufsize > 0 {
        let mut buf = vec![0u8; bufsize as usize];
        let err = lister.listxattr_second(&c_path, &mut buf, bufsize);

        if err < 0 {
            return Err(io::Error::last_os_error());
        }

        if err > 0 {
            // End indicies of the attribute names
            // the buffer contains 0-terminates c-strings
            let idx = buf.iter().enumerate().filter_map(|(i, v)|
                if *v == 0 { Some(i) } else { None }
            );
            let mut start = 0;

            for end in idx {
                let c_end = end + 1; // end of the c-string (including 0)
                let size = lister.getxattr(&c_path, &buf[start..c_end]);

                if size > 0 {
                    names.push(Attribute {
                        name: lister.translate_attribute_name(&buf[start..end]),
                        size: size as usize
                    });
                }

                start = c_end;
            }

        }

    }
    Ok(names)
}

#[cfg(target_os = "macos")]
mod lister {
    use std::ffi::CString;
    use libc::{c_int, size_t, ssize_t, c_char, c_void, uint32_t};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, namebuf: *mut c_char,
            size: size_t, options: c_int
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t, position: uint32_t,
            options: c_int
        ) -> ssize_t;
    }

    pub struct Lister {
        c_flags: c_int,
    }

    impl Lister {
        pub fn new(do_follow: FollowSymlinks) -> Lister {
            let c_flags: c_int = match do_follow {
                FollowSymlinks::Yes => 0x0001,
                FollowSymlinks::No  => 0x0000,
            };

            Lister { c_flags: c_flags }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            use std::str::from_utf8_unchecked;

            unsafe {
                from_utf8_unchecked(input).into()
            }
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            unsafe {
                listxattr(c_path.as_ptr(), ptr::null_mut(), 0, self.c_flags)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t, self.c_flags
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            unsafe {
                getxattr(
                    c_path.as_ptr(),
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0, 0, self.c_flags
                )
            }
        }
    }
}

#[cfg(target_os = "linux")]
mod lister {
    use std::ffi::CString;
    use libc::{size_t, ssize_t, c_char, c_void};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn llistxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;

        fn lgetxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;
    }

    pub struct Lister {
        follow_symlinks: FollowSymlinks,
    }

    impl Lister {
        pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
            Lister { follow_symlinks: follow_symlinks }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            String::from_utf8_lossy(input).into_owned()
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(c_path.as_ptr() as *const _, ptr::null_mut(), 0)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            let getxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => getxattr,
                FollowSymlinks::No  => lgetxattr,
            };

            unsafe {
                getxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0
                )
            }
        }
    }
}
<commit_msg>Switch to padding reference as variable not consumed<commit_after>//! Extended attribute support for Darwin and Linux systems.
#![allow(trivial_casts)]  // for ARM
extern crate libc;

use std::io;
use std::path::Path;

pub const ENABLED: bool = cfg!(feature="git") && cfg!(any(target_os="macos", target_os="linux"));

pub trait FileAttributes {
    fn attributes(&self) -> io::Result<Vec<Attribute>>;
    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>>;
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::Yes), self)
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        list_attrs(&lister::Lister::new(FollowSymlinks::No), self)
    }
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
impl FileAttributes for Path {
    fn attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }

    fn symlink_attributes(&self) -> io::Result<Vec<Attribute>> {
        Ok(vec![])
    }
}

/// Attributes which can be passed to `Attribute::list_with_flags`
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[derive(Copy, Clone)]
pub enum FollowSymlinks {
    Yes,
    No
}

/// Extended attribute
#[derive(Debug, Clone)]
pub struct Attribute {
    pub name: String,
    pub size: usize,
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn list_attrs(lister: &lister::Lister, path: &Path) -> io::Result<Vec<Attribute>> {
    use std::ffi::CString;

    let c_path = match path.to_str().and_then(|s| { CString::new(s).ok() }) {
        Some(cstring) => cstring,
        None => return Err(io::Error::new(io::ErrorKind::Other, "Error: path somehow contained a NUL?")),
    };

    let mut names = Vec::new();
    let bufsize = lister.listxattr_first(&c_path);

    if bufsize < 0 {
        return Err(io::Error::last_os_error());
    }
    else if bufsize > 0 {
        let mut buf = vec![0u8; bufsize as usize];
        let err = lister.listxattr_second(&c_path, &mut buf, bufsize);

        if err < 0 {
            return Err(io::Error::last_os_error());
        }

        if err > 0 {
            // End indicies of the attribute names
            // the buffer contains 0-terminates c-strings
            let idx = buf.iter().enumerate().filter_map(|(i, v)|
                if *v == 0 { Some(i) } else { None }
            );
            let mut start = 0;

            for end in idx {
                let c_end = end + 1; // end of the c-string (including 0)
                let size = lister.getxattr(&c_path, &buf[start..c_end]);

                if size > 0 {
                    names.push(Attribute {
                        name: lister.translate_attribute_name(&buf[start..end]),
                        size: size as usize
                    });
                }

                start = c_end;
            }

        }

    }
    Ok(names)
}

#[cfg(target_os = "macos")]
mod lister {
    use std::ffi::CString;
    use libc::{c_int, size_t, ssize_t, c_char, c_void, uint32_t};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, namebuf: *mut c_char,
            size: size_t, options: c_int
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t, position: uint32_t,
            options: c_int
        ) -> ssize_t;
    }

    pub struct Lister {
        c_flags: c_int,
    }

    impl Lister {
        pub fn new(do_follow: FollowSymlinks) -> Lister {
            let c_flags: c_int = match do_follow {
                FollowSymlinks::Yes => 0x0001,
                FollowSymlinks::No  => 0x0000,
            };

            Lister { c_flags: c_flags }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            use std::str::from_utf8_unchecked;

            unsafe {
                from_utf8_unchecked(input).into()
            }
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            unsafe {
                listxattr(c_path.as_ptr(), ptr::null_mut(), 0, self.c_flags)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            unsafe {
                listxattr(
                    c_path.as_ptr(),
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t, self.c_flags
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            unsafe {
                getxattr(
                    c_path.as_ptr(),
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0, 0, self.c_flags
                )
            }
        }
    }
}

#[cfg(target_os = "linux")]
mod lister {
    use std::ffi::CString;
    use libc::{size_t, ssize_t, c_char, c_void};
    use super::FollowSymlinks;
    use std::ptr;

    extern "C" {
        fn listxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn llistxattr(
            path: *const c_char, list: *mut c_char, size: size_t
        ) -> ssize_t;

        fn getxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;

        fn lgetxattr(
            path: *const c_char, name: *const c_char,
            value: *mut c_void, size: size_t
        ) -> ssize_t;
    }

    pub struct Lister {
        follow_symlinks: FollowSymlinks,
    }

    impl Lister {
        pub fn new(follow_symlinks: FollowSymlinks) -> Lister {
            Lister { follow_symlinks: follow_symlinks }
        }

        pub fn translate_attribute_name(&self, input: &[u8]) -> String {
            String::from_utf8_lossy(input).into_owned()
        }

        pub fn listxattr_first(&self, c_path: &CString) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(c_path.as_ptr() as *const _, ptr::null_mut(), 0)
            }
        }

        pub fn listxattr_second(&self, c_path: &CString, buf: &mut Vec<u8>, bufsize: ssize_t) -> ssize_t {
            let listxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => listxattr,
                FollowSymlinks::No  => llistxattr,
            };

            unsafe {
                listxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_mut_ptr() as *mut c_char,
                    bufsize as size_t
                )
            }
        }

        pub fn getxattr(&self, c_path: &CString, buf: &[u8]) -> ssize_t {
            let getxattr = match self.follow_symlinks {
                FollowSymlinks::Yes => getxattr,
                FollowSymlinks::No  => lgetxattr,
            };

            unsafe {
                getxattr(
                    c_path.as_ptr() as *const _,
                    buf.as_ptr() as *const c_char,
                    ptr::null_mut(), 0
                )
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// 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.

use futures::{future, unsync, Future, IntoFuture, Sink, Stream};
use futures::future::Either;
use futures::sync::mpsc;
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_retry::Retry;
use tokio_retry::strategy::{jitter, FixedInterval};

use std::net::SocketAddr;
use std::time::Duration;
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use messages::{Any, Connect, RawMessage};
use node::{ExternalMessage, NodeTimeout};
use helpers::Milliseconds;

use super::error::{into_other, log_error, other_error, result_ok};
use super::codec::MessagesCodec;
use super::{EventsAggregator, EventHandler};

#[derive(Debug)]
pub enum NetworkEvent {
    MessageReceived(SocketAddr, RawMessage),
    PeerConnected(SocketAddr, Connect),
    PeerDisconnected(SocketAddr),
}

#[derive(Debug, Clone)]
pub enum NetworkRequest {
    SendMessage(SocketAddr, RawMessage),
    DisconnectWithPeer(SocketAddr),
    Shutdown,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u64>,
    pub tcp_connect_retry_timeout: Milliseconds,
    pub tcp_connect_max_retries: u64,
}

impl Default for NetworkConfiguration {
    fn default() -> NetworkConfiguration {
        NetworkConfiguration {
            max_incoming_connections: 128,
            max_outgoing_connections: 128,
            tcp_keep_alive: None,
            tcp_nodelay: false,
            tcp_connect_retry_timeout: 15_000,
            tcp_connect_max_retries: 10,
        }
    }
}

#[derive(Debug)]
pub struct HandlerPart<H: EventHandler> {
    pub handler: H,
    pub timeout_rx: mpsc::Receiver<NodeTimeout>,
    pub network_rx: mpsc::Receiver<NetworkEvent>,
    pub api_rx: mpsc::Receiver<ExternalMessage>,
}

#[derive(Debug)]
pub struct NetworkPart {
    pub listen_address: SocketAddr,
    pub network_config: NetworkConfiguration,
    pub network_requests: (mpsc::Sender<NetworkRequest>, mpsc::Receiver<NetworkRequest>),
    pub network_tx: mpsc::Sender<NetworkEvent>,
}

#[derive(Debug, Default, Clone)]
struct ConnectionsPool {
    inner: Rc<RefCell<HashMap<SocketAddr, mpsc::Sender<RawMessage>>>>,
}

impl ConnectionsPool {
    fn new() -> ConnectionsPool {
        ConnectionsPool::default()
    }

    fn insert(&self, peer: SocketAddr, sender: &mpsc::Sender<RawMessage>) {
        self.inner.borrow_mut().insert(peer, sender.clone());
    }

    fn remove(&self, peer: &SocketAddr) {
        self.inner.borrow_mut().remove(peer);
    }

    fn get(&self, peer: SocketAddr) -> Option<mpsc::Sender<RawMessage>> {
        self.inner.borrow_mut().get(&peer).cloned()
    }

    fn len(&self) -> usize {
        self.inner.borrow_mut().len()
    }
}

impl<H: EventHandler + 'static> HandlerPart<H> {
    pub fn run(self) -> Box<Future<Item=(), Error=()>> {
        let mut handler = self.handler;

        let fut = EventsAggregator::new(self.timeout_rx, self.network_rx, self.api_rx)
            .for_each(move |event| {
                handler.handle_event(event);
                Ok(())
            });

        Box::new(fut)
    }
}

impl NetworkPart {
    pub fn run(self, handle_orig: Handle) -> Box<Future<Item=(), Error=()>> {
        let network_config = self.network_config;
        // Cancelation token
        let (cancel_sender, cancel_handler) = unsync::oneshot::channel();
        let mut cancel_sender = Some(cancel_sender);
        // Outgoing connections
        let outgoing_connections = ConnectionsPool::new();
        let network_tx = self.network_tx.clone();
        // Outgoing connections limiter
        let outgoing_connections_limit = network_config.max_outgoing_connections;
        let handle = handle_orig.clone();
        let requests_handle = self.network_requests.1.for_each(move |request| {
            let handle = handle.clone();
            let outgoing_connections = outgoing_connections.clone();
            match request {
                NetworkRequest::SendMessage(peer, msg) => {
                    let network_tx_cloned = network_tx.clone();
                    let conn_tx = outgoing_connections.get(peer)
                        .or_else(move || {
                            if outgoing_connections.len() >= outgoing_connections_limit {
                                warn!(
                                    "Rejected outgoing connection with peer={}, \
                                     connections limit reached.",
                                    peer
                                );
                                None
                            } else {
                                // Register outgoing channel.
                                let (conn_tx, conn_rx) = mpsc::channel(10);
                                outgoing_connections.insert(peer, &conn_tx);
                                // Enable retry feature for outgoing connection.
                                let timeout = network_config.tcp_connect_retry_timeout;
                                let max_tries = network_config.tcp_connect_max_retries as usize;
                                let strategy = FixedInterval::from_millis(timeout)
                                    .map(jitter)
                                    .take(max_tries);
                                let handle_cloned = handle.clone();
                                let action = move || {
                                    TcpStream::connect(&peer, &handle_cloned)
                                };
                                let connect_handle = Retry::spawn(handle.clone(), strategy, action)
                                    .map_err(into_other)
                                    // Configure socket
                                    .and_then(move |sock| {
                                        sock.set_nodelay(network_config.tcp_nodelay)?;
                                        let duration =
                                            network_config.tcp_keep_alive.map(Duration::from_millis);
                                        sock.set_keepalive(duration)?;
                                        Ok(sock)
                                    })
                                    // Connect socket with the outgoing channel
                                    .and_then(move |sock| {
                                        trace!("Established connection with peer={}", peer);

                                        let stream = sock.framed(MessagesCodec);
                                        let (sink, stream) = stream.split();

                                        let writer = conn_rx
                                            .map_err(|_| other_error("Can't send data into socket"))
                                            .forward(sink);
                                        let reader = stream.for_each(result_ok);

                                        reader
                                            .select2(writer)
                                            .map_err(|_| other_error("Socket error"))
                                            .and_then(|res| match res {
                                                Either::A((_, _reader)) => Ok("by reader"),
                                                Either::B((_, _writer)) => Ok("by writer"),
                                            })
                                    })
                                    .then(move |res| {
                                        trace!("Connection with peer={} closed, reason={:?}", peer, res);
                                        outgoing_connections.clone().remove(&peer);
                                        network_tx_cloned
                                            .clone()
                                            .send(NetworkEvent::PeerDisconnected(peer))
                                            .map(drop)
                                    })
                                    .map_err(log_error);
                                handle.spawn(connect_handle);
                                Some(conn_tx)
                            }
                        });
                    if let Some(conn_tx) = conn_tx {
                        let send_handle = conn_tx.send(msg).map_err(log_error).map(drop);
                        tobox(send_handle)
                    } else {
                        let event = NetworkEvent::PeerDisconnected(peer);
                        let fut = network_tx.clone().send(event).map_err(log_error)
                            .into_future();
                        tobox(fut)
                    }
                }
                NetworkRequest::DisconnectWithPeer(peer) => {
                    outgoing_connections.remove(&peer);
                    let event = NetworkEvent::PeerDisconnected(peer);
                    let fut = network_tx.clone().send(event).map(drop).map_err(log_error);
                    tobox(fut)
                }
                // Immediately stop the event loop.
                NetworkRequest::Shutdown => {
                    let fut = cancel_sender
                        .take()
                        .ok_or_else(|| other_error("Shutdown twice"))
                        .and_then(|sender| {
                            sender.send(()).map_err(
                                |_| other_error("Can't send shutdown signal"),
                            )
                        })
                        .map_err(log_error)
                        .into_future();
                    tobox(fut)
                }
            }
        });

        // Incoming connections limiter
        let incoming_connections_limit = network_config.max_incoming_connections;
        let incoming_connections_counter: Rc<()> = Rc::default();
        // Incoming connections handler
        // TODO Don't use unwrap here!
        let listener = TcpListener::bind(&self.listen_address, &handle_orig).unwrap();
        let network_tx = self.network_tx.clone();
        let server = listener
            .incoming()
            .for_each(move |(sock, addr)| {
                // Increment reference counter
                let holder = Rc::downgrade(&incoming_connections_counter);
                // Check incoming connections count
                let connections_count = Rc::weak_count(&incoming_connections_counter);
                if connections_count > incoming_connections_limit {
                    warn!(
                        "Rejected incoming connection with peer={}, \
                         connections limit reached.",
                        addr
                    );
                    tobox(future::ok(()))
                } else {
                    trace!("Accepted incoming connection with peer={}", addr);
                    let stream = sock.framed(MessagesCodec);
                    let (_, stream) = stream.split();
                    let network_tx = network_tx.clone();
                    let connection_handler = stream
                        .into_future()
                        .map_err(|e| e.0)
                        .and_then(move |(raw, stream)| match raw.map(Any::from_raw) {
                            Some(Ok(Any::Connect(msg))) => Ok((msg, stream)),
                            Some(Ok(other)) => Err(other_error(&format!(
                                "First message is not Connect, got={:?}",
                                other
                            ))),
                            Some(Err(e)) => Err(into_other(e)),
                            None => Err(other_error("Incoming socket closed")),
                        })
                        .and_then(move |(connect, stream)| {
                            trace!("Received handshake message={:?}", connect);
                            let event = NetworkEvent::PeerConnected(addr, connect);
                            let stream = network_tx
                                .clone()
                                .send(event)
                                .map_err(into_other)
                                .and_then(move |_| Ok(stream))
                                .flatten_stream();

                            stream.for_each(move |raw| {
                                let event = NetworkEvent::MessageReceived(addr, raw);
                                network_tx.clone().send(event).map_err(into_other).map(drop)
                            })
                        })
                        .map(|_| {
                            // Ensure that holder lives until the stream ends.
                            let _holder = holder;
                        });
                    tobox(connection_handler)
                }
            })
            .map_err(log_error);

        let cancel_handler = cancel_handler.map_err(drop);
        let tasks = vec![
            tobox(server),
            tobox(requests_handle),
            tobox(cancel_handler)
        ];
        tobox(future::join_all(tasks))
    }
}

fn tobox<F: Future + 'static>(f: F) -> Box<Future<Item=(), Error=F::Error>> {
    let fut = f.map(|_| ());
    Box::new(fut)
}

<commit_msg>Fix cancel message handling<commit_after>// Copyright 2017 The Exonum Team
//
// 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.

use futures::{future, unsync, Future, IntoFuture, Sink, Stream};
use futures::future::Either;
use futures::sync::mpsc;
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor::Handle;
use tokio_io::AsyncRead;
use tokio_retry::Retry;
use tokio_retry::strategy::{jitter, FixedInterval};

use std::net::SocketAddr;
use std::time::Duration;
use std::collections::HashMap;
use std::rc::Rc;
use std::cell::RefCell;

use messages::{Any, Connect, RawMessage};
use node::{ExternalMessage, NodeTimeout};
use helpers::Milliseconds;

use super::error::{into_other, log_error, other_error, result_ok};
use super::codec::MessagesCodec;
use super::{EventsAggregator, EventHandler};

#[derive(Debug)]
pub enum NetworkEvent {
    MessageReceived(SocketAddr, RawMessage),
    PeerConnected(SocketAddr, Connect),
    PeerDisconnected(SocketAddr),
}

#[derive(Debug, Clone)]
pub enum NetworkRequest {
    SendMessage(SocketAddr, RawMessage),
    DisconnectWithPeer(SocketAddr),
    Shutdown,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct NetworkConfiguration {
    // TODO: think more about config parameters
    pub max_incoming_connections: usize,
    pub max_outgoing_connections: usize,
    pub tcp_nodelay: bool,
    pub tcp_keep_alive: Option<u64>,
    pub tcp_connect_retry_timeout: Milliseconds,
    pub tcp_connect_max_retries: u64,
}

impl Default for NetworkConfiguration {
    fn default() -> NetworkConfiguration {
        NetworkConfiguration {
            max_incoming_connections: 128,
            max_outgoing_connections: 128,
            tcp_keep_alive: None,
            tcp_nodelay: false,
            tcp_connect_retry_timeout: 15_000,
            tcp_connect_max_retries: 10,
        }
    }
}

#[derive(Debug)]
pub struct HandlerPart<H: EventHandler> {
    pub handler: H,
    pub timeout_rx: mpsc::Receiver<NodeTimeout>,
    pub network_rx: mpsc::Receiver<NetworkEvent>,
    pub api_rx: mpsc::Receiver<ExternalMessage>,
}

#[derive(Debug)]
pub struct NetworkPart {
    pub listen_address: SocketAddr,
    pub network_config: NetworkConfiguration,
    pub network_requests: (mpsc::Sender<NetworkRequest>, mpsc::Receiver<NetworkRequest>),
    pub network_tx: mpsc::Sender<NetworkEvent>,
}

#[derive(Debug, Default, Clone)]
struct ConnectionsPool {
    inner: Rc<RefCell<HashMap<SocketAddr, mpsc::Sender<RawMessage>>>>,
}

impl ConnectionsPool {
    fn new() -> ConnectionsPool {
        ConnectionsPool::default()
    }

    fn insert(&self, peer: SocketAddr, sender: &mpsc::Sender<RawMessage>) {
        self.inner.borrow_mut().insert(peer, sender.clone());
    }

    fn remove(&self, peer: &SocketAddr) {
        self.inner.borrow_mut().remove(peer);
    }

    fn get(&self, peer: SocketAddr) -> Option<mpsc::Sender<RawMessage>> {
        self.inner.borrow_mut().get(&peer).cloned()
    }

    fn len(&self) -> usize {
        self.inner.borrow_mut().len()
    }
}

impl<H: EventHandler + 'static> HandlerPart<H> {
    pub fn run(self) -> Box<Future<Item=(), Error=()>> {
        let mut handler = self.handler;

        let fut = EventsAggregator::new(self.timeout_rx, self.network_rx, self.api_rx)
            .for_each(move |event| {
                handler.handle_event(event);
                Ok(())
            });

        Box::new(fut)
    }
}

impl NetworkPart {
    pub fn run(self, handle_orig: Handle) -> Box<Future<Item=(), Error=()>> {
        let network_config = self.network_config;
        // Cancelation token
        let (cancel_sender, cancel_handler) = unsync::oneshot::channel();
        let mut cancel_sender = Some(cancel_sender);
        // Outgoing connections
        let outgoing_connections = ConnectionsPool::new();
        let network_tx = self.network_tx.clone();
        // Outgoing connections limiter
        let outgoing_connections_limit = network_config.max_outgoing_connections;
        let handle = handle_orig.clone();
        let requests_handle = self.network_requests.1.for_each(move |request| {
            let handle = handle.clone();
            let outgoing_connections = outgoing_connections.clone();
            match request {
                NetworkRequest::SendMessage(peer, msg) => {
                    let network_tx_cloned = network_tx.clone();
                    let conn_tx = outgoing_connections.get(peer)
                        .or_else(move || {
                            if outgoing_connections.len() >= outgoing_connections_limit {
                                warn!(
                                    "Rejected outgoing connection with peer={}, \
                                     connections limit reached.",
                                    peer
                                );
                                None
                            } else {
                                // Register outgoing channel.
                                let (conn_tx, conn_rx) = mpsc::channel(10);
                                outgoing_connections.insert(peer, &conn_tx);
                                // Enable retry feature for outgoing connection.
                                let timeout = network_config.tcp_connect_retry_timeout;
                                let max_tries = network_config.tcp_connect_max_retries as usize;
                                let strategy = FixedInterval::from_millis(timeout)
                                    .map(jitter)
                                    .take(max_tries);
                                let handle_cloned = handle.clone();
                                let action = move || {
                                    TcpStream::connect(&peer, &handle_cloned)
                                };
                                let connect_handle = Retry::spawn(handle.clone(), strategy, action)
                                    .map_err(into_other)
                                    // Configure socket
                                    .and_then(move |sock| {
                                        sock.set_nodelay(network_config.tcp_nodelay)?;
                                        let duration =
                                            network_config.tcp_keep_alive.map(Duration::from_millis);
                                        sock.set_keepalive(duration)?;
                                        Ok(sock)
                                    })
                                    // Connect socket with the outgoing channel
                                    .and_then(move |sock| {
                                        trace!("Established connection with peer={}", peer);

                                        let stream = sock.framed(MessagesCodec);
                                        let (sink, stream) = stream.split();

                                        let writer = conn_rx
                                            .map_err(|_| other_error("Can't send data into socket"))
                                            .forward(sink);
                                        let reader = stream.for_each(result_ok);

                                        reader
                                            .select2(writer)
                                            .map_err(|_| other_error("Socket error"))
                                            .and_then(|res| match res {
                                                Either::A((_, _reader)) => Ok("by reader"),
                                                Either::B((_, _writer)) => Ok("by writer"),
                                            })
                                    })
                                    .then(move |res| {
                                        trace!("Connection with peer={} closed, reason={:?}", peer, res);
                                        outgoing_connections.clone().remove(&peer);
                                        network_tx_cloned
                                            .clone()
                                            .send(NetworkEvent::PeerDisconnected(peer))
                                            .map(drop)
                                    })
                                    .map_err(log_error);
                                handle.spawn(connect_handle);
                                Some(conn_tx)
                            }
                        });
                    if let Some(conn_tx) = conn_tx {
                        let send_handle = conn_tx.send(msg).map_err(log_error).map(drop);
                        tobox(send_handle)
                    } else {
                        let event = NetworkEvent::PeerDisconnected(peer);
                        let fut = network_tx.clone().send(event).map_err(log_error)
                            .into_future();
                        tobox(fut)
                    }
                }
                NetworkRequest::DisconnectWithPeer(peer) => {
                    outgoing_connections.remove(&peer);
                    let event = NetworkEvent::PeerDisconnected(peer);
                    let fut = network_tx.clone().send(event).map(drop).map_err(log_error);
                    tobox(fut)
                }
                // Immediately stop the event loop.
                NetworkRequest::Shutdown => {
                    let fut = cancel_sender
                        .take()
                        .ok_or_else(|| other_error("Shutdown twice"))
                        .and_then(|sender| {
                            sender.send(()).map_err(
                                |_| other_error("Can't send shutdown signal"),
                            )
                        })
                        .map_err(log_error)
                        .into_future();
                    tobox(fut)
                }
            }
        });

        // Incoming connections limiter
        let incoming_connections_limit = network_config.max_incoming_connections;
        let incoming_connections_counter: Rc<()> = Rc::default();
        // Incoming connections handler
        // TODO Don't use unwrap here!
        let listener = TcpListener::bind(&self.listen_address, &handle_orig).unwrap();
        let network_tx = self.network_tx.clone();
        let server = listener
            .incoming()
            .for_each(move |(sock, addr)| {
                // Increment reference counter
                let holder = Rc::downgrade(&incoming_connections_counter);
                // Check incoming connections count
                let connections_count = Rc::weak_count(&incoming_connections_counter);
                if connections_count > incoming_connections_limit {
                    warn!(
                        "Rejected incoming connection with peer={}, \
                         connections limit reached.",
                        addr
                    );
                    tobox(future::ok(()))
                } else {
                    trace!("Accepted incoming connection with peer={}", addr);
                    let stream = sock.framed(MessagesCodec);
                    let (_, stream) = stream.split();
                    let network_tx = network_tx.clone();
                    let connection_handler = stream
                        .into_future()
                        .map_err(|e| e.0)
                        .and_then(move |(raw, stream)| match raw.map(Any::from_raw) {
                            Some(Ok(Any::Connect(msg))) => Ok((msg, stream)),
                            Some(Ok(other)) => Err(other_error(&format!(
                                "First message is not Connect, got={:?}",
                                other
                            ))),
                            Some(Err(e)) => Err(into_other(e)),
                            None => Err(other_error("Incoming socket closed")),
                        })
                        .and_then(move |(connect, stream)| {
                            trace!("Received handshake message={:?}", connect);
                            let event = NetworkEvent::PeerConnected(addr, connect);
                            let stream = network_tx
                                .clone()
                                .send(event)
                                .map_err(into_other)
                                .and_then(move |_| Ok(stream))
                                .flatten_stream();

                            stream.for_each(move |raw| {
                                let event = NetworkEvent::MessageReceived(addr, raw);
                                network_tx.clone().send(event).map_err(into_other).map(drop)
                            })
                        })
                        .map(|_| {
                            // Ensure that holder lives until the stream ends.
                            let _holder = holder;
                        });
                    tobox(connection_handler)
                }
            })
            .map_err(log_error);

        let cancel_handler = cancel_handler.map_err(drop);
        let fut = server.join(requests_handle)
            .map(drop)
            .select(cancel_handler)
            .map_err(drop);

        tobox(fut)
    }
}

fn tobox<F: Future + 'static>(f: F) -> Box<Future<Item=(), Error=F::Error>> {
    let fut = f.map(|_| ());
    Box::new(fut)
}

<|endoftext|>


<commit_before>#![feature(fs_walk)]

extern crate rustc_serialize;
extern crate eve;

use std::env;
use std::fs::{OpenOptions, walk_dir};
use std::io::prelude::*;
use rustc_serialize::json::Json;

use eve::server::*;
use eve::value::*;

#[test]
fn test_examples() {
    let inputs = walk_dir("./test-inputs").unwrap().collect::<Vec<_>>();
    let outputs = walk_dir("./test-outputs").unwrap().collect::<Vec<_>>();
    assert_eq!(inputs.len(), outputs.len());
    for (input_entry, output_entry) in inputs.into_iter().zip(outputs.into_iter()) {
        let input_filename = input_entry.unwrap().path().to_str().unwrap().to_owned();
        let output_filename = output_entry.unwrap().path().to_str().unwrap().to_owned();
        println!("Testing {:?} against {:?}", input_filename, output_filename);
        let flow = load(&input_filename[..]);
        let mut output_string = String::new();
        let mut output_file = OpenOptions::new().open(output_filename).unwrap();
        output_file.read_to_string(&mut output_string).unwrap();
        let output = Json::from_str(&output_string).unwrap();
        for (view_id, json) in output.as_object().unwrap().iter() {
            let test_rows: Vec<Vec<Value>> = flow.get_output(&view_id[..]).index.clone().into_iter().collect();
            let output_rows: Vec<Vec<Value>> = FromJson::from_json(json);
            assert_eq!(test_rows, output_rows);
        }
    }
}

#[allow(dead_code)]
fn main() {
	for argument in env::args() {
    	match &*argument {
    		"clean" => {
    			OpenOptions::new().create(true).truncate(true).open("./events").unwrap();
    			()
    		},
    		_ => continue,
    	}
	}

    run()
}<commit_msg>Fix warnings<commit_after>extern crate rustc_serialize;
extern crate eve;

use std::env;
use std::fs::OpenOptions;

use eve::server::*;

#[test]
fn test_examples() {
    let inputs = walk_dir("./test-inputs").unwrap().collect::<Vec<_>>();
    let outputs = walk_dir("./test-outputs").unwrap().collect::<Vec<_>>();
    assert_eq!(inputs.len(), outputs.len());
    for (input_entry, output_entry) in inputs.into_iter().zip(outputs.into_iter()) {
        let input_filename = input_entry.unwrap().path().to_str().unwrap().to_owned();
        let output_filename = output_entry.unwrap().path().to_str().unwrap().to_owned();
        println!("Testing {:?} against {:?}", input_filename, output_filename);
        let flow = load(&input_filename[..]);
        let mut output_string = String::new();
        let mut output_file = OpenOptions::new().open(output_filename).unwrap();
        output_file.read_to_string(&mut output_string).unwrap();
        let output = Json::from_str(&output_string).unwrap();
        for (view_id, json) in output.as_object().unwrap().iter() {
            let test_rows: Vec<Vec<Value>> = flow.get_output(&view_id[..]).index.clone().into_iter().collect();
            let output_rows: Vec<Vec<Value>> = FromJson::from_json(json);
            assert_eq!(test_rows, output_rows);
        }
    }
}

#[allow(dead_code)]
fn main() {
	for argument in env::args() {
    	match &*argument {
    		"clean" => {
    			OpenOptions::new().create(true).truncate(true).open("./events").unwrap();
    			()
    		},
    		_ => continue,
    	}
	}

    run()
}<|endoftext|>


<commit_before>struct Interval(f32, f32);

fn f(x: f32) -> f32 {
    return x * x - x - 1.0;
}

fn midpoint_rule(i: Interval, n: i32) -> f32 {
    let h = (i.1 - i.0) / n as f32;
    let mut r = 0.0;
    let mut x = i.0 + h / 2.0;

    for _ in 0..n {
        r += f(x);
        x += h;
    }
    return r * h;
}

fn trapezoid_rule(i: Interval, n: i32) -> f32 {
    let h = (i.1 - i.0) / n as f32;
    let mut r = 0.0;
    let mut x = i.0 + h;
    let mut y1 = f(i.0);

    for _ in 0..n {
        let y2 = f(x);
        r += y1 + y2;
        x += h;
        y1 = y2;
    }
    return r * h / 2.0;
}

fn main() {
    println!("Midpoint Rule: {}", midpoint_rule(Interval(-2.0, 4.0), 1000));
    println!("Trapezoid Rule: {}", trapezoid_rule(Interval(-2.0, 4.0), 1000));
}
<commit_msg>Update<commit_after>trait Integral {
    fn method(&self) -> String;
    fn calc(&self) -> f32;
    fn print(&self) {
        println!("{}: {}", self.method(), self.calc());
    }
}

struct Interval(f32, f32);

struct MidpointRule {
    function: fn(f32) -> f32,
    interval: Interval,
    step: i32,
}

impl Integral for MidpointRule {
    fn method(&self) -> String {
        return String::from("Midpoint Rule");
    }

    fn calc(&self) -> f32 {
        let a = self.interval.0;
        let b = self.interval.1;
        let n = self.step;
        let h = (b - a) / n as f32;
        let mut r = 0.0;
        let mut x = a + h / 2.0;

        for _ in 0..n {
            r += (self.function)(x);
            x += h;
        }
        return r * h;
    }
}

struct TrapezoidRule {
    function: fn(f32) -> f32,
    interval: Interval,
    step: i32,
}

impl Integral for TrapezoidRule {
    fn method(&self) -> String {
        return String::from("Trapezoid Rule");
    }

    fn calc(&self) -> f32 {
        let a = self.interval.0;
        let b = self.interval.1;
        let n = self.step;
        let h = (b - a) / n as f32;
        let mut r = 0.0;
        let mut x = a + h;
        let mut y1 = (self.function)(a);

        for _ in 0..n {
            let y2 = (self.function)(x);
            r += y1 + y2;
            x += h;
            y1 = y2;
        }
        return r * h / 2.0;
    }
}

fn main() {
    fn f(x: f32) -> f32 {
        return x * x - x - 1.0;
    }

    MidpointRule {
        function: f,
        interval: Interval(-2.0f32, 4.0f32),
        step: 1000,
    }.print();

    TrapezoidRule {
        function: f,
        interval: Interval(-2.0f32, 4.0f32),
        step: 1000,
    }.print();
}
<|endoftext|>


<commit_before>export FreeTypeNativeFont, with_test_native_font;

import vec_as_buf = vec::as_buf;
import result::{result, ok, err};
import ptr::{addr_of, null};
import unsafe::reinterpret_cast;
import glyph::GlyphIndex;
import azure::freetype;
import freetype::{ FT_Error, FT_Library, FT_Face, FT_Long, FT_ULong, FT_UInt, FT_GlyphSlot };
import freetype::bindgen::{
    FT_Init_FreeType,
    FT_Done_FreeType,
    FT_New_Memory_Face,
    FT_Done_Face,
    FT_Get_Char_Index,
    FT_Load_Glyph,
    FT_Set_Char_Size
};

class FreeTypeNativeFont/& {
    let face: FT_Face;

    new(face: FT_Face) {
        assert face.is_not_null();
        self.face = face;
    }

    drop {
        assert self.face.is_not_null();
        if !FT_Done_Face(self.face).succeeded() {
            fail "FT_Done_Face failed";
        }
    }

    fn glyph_index(codepoint: char) -> option<GlyphIndex> {
        assert self.face.is_not_null();
        let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong);
        ret if idx != 0 as FT_UInt {
            some(idx as GlyphIndex)
        } else {
            #warn("Invalid codepoint: %?", codepoint);
            none
        };
    }

    // FIXME: What unit is this returning? Let's have a custom type
    fn glyph_h_advance(glyph: GlyphIndex) -> option<int> {
        assert self.face.is_not_null();
        let res =  FT_Load_Glyph(self.face, glyph as FT_UInt, 0);
        if res.succeeded() {
            unsafe {
                let void_glyph = (*self.face).glyph;
                let slot: FT_GlyphSlot = reinterpret_cast(void_glyph);
                assert slot.is_not_null();
                let advance = (*slot).metrics.horiAdvance;
                #debug("h_advance for %? is %?", glyph, advance);
                // FIXME: Dividing by 64 converts to pixels, which
                // is not the unit we should be using
                ret some((advance / 64) as int);
            }
        } else {
            #warn("Unable to load glyph %?. reason: %?", glyph, res);
            ret none;
        }
    }
}

fn create(lib: FT_Library, buf: &[u8]) -> result<FreeTypeNativeFont, ()> {
    assert lib.is_not_null();
    let face: FT_Face = null();
    ret vec_as_buf(*buf) {|cbuf|
        if FT_New_Memory_Face(lib, cbuf, (*buf).len() as FT_Long,
                              0 as FT_Long, addr_of(face)).succeeded() {
            // FIXME: These values are placeholders
            let res = FT_Set_Char_Size(face, 0, 20*64, 0, 72);
            if !res.succeeded() { fail "unable to set font char size" }
            ok(FreeTypeNativeFont(face))
        } else {
            err(())
        }
    }
}

impl methods for FT_Error {
    fn succeeded() -> bool { self == 0 as FT_Error }
}

fn with_test_native_font(f: fn@(nf: &NativeFont)) {
    import font::test_font_bin;
    import unwrap_result = result::unwrap;

    with_lib { |lib|
        let buf = test_font_bin();
        let font = unwrap_result(create(lib, &buf));
        f(&font);
    }
}

#[cfg(test)]
fn with_lib(f: fn@(FT_Library)) {
    let lib: FT_Library = null();
    assert FT_Init_FreeType(addr_of(lib)).succeeded();
    f(lib);
    FT_Done_FreeType(lib);
}

#[test]
fn create_should_return_err_if_buf_is_bogus() {
    with_lib { |lib|
        let buf = &[];
        assert create(lib, buf).is_err();
    }
}
<commit_msg>Fix Linux build<commit_after>export FreeTypeNativeFont, with_test_native_font;

import vec_as_buf = vec::as_buf;
import result::{result, ok, err};
import ptr::{addr_of, null};
import unsafe::reinterpret_cast;
import glyph::GlyphIndex;
import azure::freetype;
import freetype::{ FT_Error, FT_Library, FT_Face, FT_Long, FT_ULong, FT_UInt, FT_GlyphSlot };
import freetype::bindgen::{
    FT_Init_FreeType,
    FT_Done_FreeType,
    FT_New_Memory_Face,
    FT_Done_Face,
    FT_Get_Char_Index,
    FT_Load_Glyph,
    FT_Set_Char_Size
};

class FreeTypeNativeFont/& {
    let face: FT_Face;

    new(face: FT_Face) {
        assert face.is_not_null();
        self.face = face;
    }

    drop {
        assert self.face.is_not_null();
        if !FT_Done_Face(self.face).succeeded() {
            fail "FT_Done_Face failed";
        }
    }

    fn glyph_index(codepoint: char) -> option<GlyphIndex> {
        assert self.face.is_not_null();
        let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong);
        ret if idx != 0 as FT_UInt {
            some(idx as GlyphIndex)
        } else {
            #warn("Invalid codepoint: %?", codepoint);
            none
        };
    }

    // FIXME: What unit is this returning? Let's have a custom type
    fn glyph_h_advance(glyph: GlyphIndex) -> option<int> {
        assert self.face.is_not_null();
        let res =  FT_Load_Glyph(self.face, glyph as FT_UInt, 0);
        if res.succeeded() {
            unsafe {
                let void_glyph = (*self.face).glyph;
                let slot: FT_GlyphSlot = reinterpret_cast(void_glyph);
                assert slot.is_not_null();
                let advance = (*slot).metrics.horiAdvance;
                #debug("h_advance for %? is %?", glyph, advance);
                // FIXME: Dividing by 64 converts to pixels, which
                // is not the unit we should be using
                ret some((advance / 64) as int);
            }
        } else {
            #warn("Unable to load glyph %?. reason: %?", glyph, res);
            ret none;
        }
    }
}

fn create(lib: FT_Library, buf: &[u8]) -> result<FreeTypeNativeFont, ()> {
    assert lib.is_not_null();
    let face: FT_Face = null();
    ret vec_as_buf(*buf) {|cbuf|
        if FT_New_Memory_Face(lib, cbuf, (*buf).len() as FT_Long,
                              0 as FT_Long, addr_of(face)).succeeded() {
            // FIXME: These values are placeholders
            let res = FT_Set_Char_Size(face, 0, 20*64, 0, 72);
            if !res.succeeded() { fail "unable to set font char size" }
            ok(FreeTypeNativeFont(face))
        } else {
            err(())
        }
    }
}

impl methods for FT_Error {
    fn succeeded() -> bool { self == 0 as FT_Error }
}

fn with_test_native_font(f: fn@(nf: &NativeFont)) {
    import font::test_font_bin;
    import unwrap_result = result::unwrap;

    with_lib { |lib|
        let buf = test_font_bin();
        let font = unwrap_result(create(lib, &buf));
        f(&font);
    }
}

fn with_lib(f: fn@(FT_Library)) {
    let lib: FT_Library = null();
    assert FT_Init_FreeType(addr_of(lib)).succeeded();
    f(lib);
    FT_Done_FreeType(lib);
}

#[test]
fn create_should_return_err_if_buf_is_bogus() {
    with_lib { |lib|
        let buf = &[];
        assert create(lib, buf).is_err();
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_dirname"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

use std::path::Path;

static NAME: &'static str = "dirname";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();
    opts.optflag("z", "zero", "separate output with NUL rather than newline");
    opts.optflag("", "help", "display this help and exit");
    opts.optflag("", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        let msg = format!("{0} {1} - strip last component from file name

Usage:
  {0} [OPTION] NAME...

Output each NAME with its last non-slash component and trailing slashes
removed; if NAME contains no  /'s,  output  '.'  (meaning  the  current
directory).", NAME, VERSION);

        print!("{}", opts.usage(&msg));
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let separator = if matches.opt_present("zero") {"\0"} else {"\n"};

    if !matches.free.is_empty() {
        for path in &matches.free {
            let p = Path::new(path);
            match p.parent() {
                Some(d) => {
                    if d.components().next() == None {
                        print!(".")
                    } else {
                        print!("{}", d.to_string_lossy());
                    }
                }
                None => {
                    if p.is_absolute() {
                        print!("/");
                    } else {
                        print!(".");
                    }
                }
            }
            print!("{}", separator);
        }
    } else {
        println!("{0}: missing operand", NAME);
        println!("Try '{0} --help' for more information.", NAME);
        return 1;
    }

    0
}
<commit_msg>dirname: in impl, make behavior for '/' fs root agnostic of host platform<commit_after>#![crate_name = "uu_dirname"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Derek Chiang <derekchiang93@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

use std::path::Path;

static NAME: &'static str = "dirname";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();
    opts.optflag("z", "zero", "separate output with NUL rather than newline");
    opts.optflag("", "help", "display this help and exit");
    opts.optflag("", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!("Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        let msg = format!("{0} {1} - strip last component from file name

Usage:
  {0} [OPTION] NAME...

Output each NAME with its last non-slash component and trailing slashes
removed; if NAME contains no  /'s,  output  '.'  (meaning  the  current
directory).", NAME, VERSION);

        print!("{}", opts.usage(&msg));
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let separator = if matches.opt_present("zero") {"\0"} else {"\n"};

    if !matches.free.is_empty() {
        for path in &matches.free {
            let p = Path::new(path);
            match p.parent() {
                Some(d) => {
                    if d.components().next() == None {
                        print!(".")
                    } else {
                        print!("{}", d.to_string_lossy());
                    }
                }
                None => {
                    if p.is_absolute() || path == "/" {
                        print!("/");
                    } else {
                        print!(".");
                    }
                }
            }
            print!("{}", separator);
        }
    } else {
        println!("{0}: missing operand", NAME);
        println!("Try '{0} --help' for more information.", NAME);
        return 1;
    }

    0
}
<|endoftext|>


<commit_before>#![crate_type = "staticlib"]

use std::ffi::CString;
use std::os::raw::c_char;

#[repr(C)]
struct mpc_state_t
{
    pos: i64,
    row: i64,
    col: i64,
}

#[repr(C)]
struct mpc_ast_t
{
    tag: *mut c_char,
    contents: *mut c_char,
    children_num: i32,
    children: *mut *mut mpc_ast_t,
}

extern
{
    fn puts(s: *const c_char);
}

#[no_mangle]
unsafe extern fn analyse()
{
    puts(CString::new("Hello world").unwrap().as_ptr());
}

#[no_mangle]
pub unsafe extern fn main()
{
    analyse();
}
<commit_msg>start with analysis<commit_after>#![crate_type = "staticlib"]
#![allow(unused_variables)]
#![allow(non_upper_case_globals)]

use std::ptr;
use std::slice;
use std::vec::Vec;
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_void;
use std::os::raw::c_char;
use std::collections::LinkedList;

#[repr(C)]
pub struct mpc_state_t
{
    pos: i64,
    row: i64,
    col: i64,
}

#[repr(C)]
pub struct mpc_ast_t
{
    tag: *mut c_char,
    contents: *mut c_char,
    state: mpc_state_t,
    children_num: i32,
    children: *const *const mpc_ast_t,
}

#[repr(C)]
pub struct vector;

//TODO map structs to C

#[repr(C)]
pub struct module
{
    name: *mut c_char,
    imports: *mut vector,
    functions: *mut vector,
    types: *mut vector,
}

#[repr(C)]
pub struct import
{
    name: *mut c_char,
    alias: *mut c_char,
    local: bool,
    w_alias: bool,
}

#[repr(C)]
pub struct function
{
    name: *mut c_char,
    public : bool,
    param_count : i32,
    params: *mut vector,
    node: *const mpc_ast_t,
}

#[repr(C)]
pub struct param
{
    p_type: symbol_type,
    name: *mut c_char,
    default: String,
    w_default: bool,
}

#[repr(C)]
pub struct symbol_type
{
    name: *mut c_char,
    indirection: i32,
}

#[repr(C)]
pub struct user_type
{
    name: *mut c_char,
    t_type: type_kind,
}

#[repr(C)]
enum type_kind
{
    ALIAS,
    STRUCT,
    ENUM,
}

impl mpc_ast_t
{
    unsafe fn get_children(&self) -> &'static[*const mpc_ast_t]
    {
        slice::from_raw_parts(self.children, self.children_num as usize)
    }
}

extern
{
    fn puts(s: *const c_char);
    fn vector_alloc() -> *mut vector;
    fn vector_init(v: *mut vector);
    fn vector_add(l: *mut vector, d: *mut c_void);
    fn vector_total(l: *mut vector, carry: i32) -> i32;
    fn vector_get(l: *mut vector, index: i32, carry: i32) -> *mut c_void;
}

const RED      :&'static str = "\x1b[31m";
const GREEN    :&'static str = "\x1b[32m";
const YELLOW   :&'static str = "\x1b[33m";
const BLUE     :&'static str = "\x1b[34m";
const MAGENTA  :&'static str = "\x1b[35m";
const CYAN     :&'static str = "\x1b[36m";
const RESET    :&'static str = "\x1b[0m";

static mut this_module: *mut module = 0 as *mut module;

#[no_mangle]
pub unsafe extern fn analyse(ast_ptr: *mut mpc_ast_t)
{
    this_module = &mut (module
        {
            name: make_string("placeholder".to_string()),
            functions: vector_alloc(),
            imports: vector_alloc(),
            types: vector_alloc(),
        }) as *mut module;

    println!("test: {} {}", CStr::from_ptr((*this_module).name).to_string_lossy().into_owned().len(),
                            read_string((*this_module).name));
    process(ast_ptr, 0);
}

pub unsafe fn process(ast_ptr: *mut mpc_ast_t, level: usize)
{

    let ast = &*ast_ptr;
    let children = slice::from_raw_parts(ast.children, ast.children_num as usize);

    for i in 0 .. ast.children_num as usize
    {
        let tag = CStr::from_ptr( (*children[i]).tag ).to_string_lossy().into_owned();
        let ref child = *children[i];

        match tag.as_ref()
        {
            "func" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag" + &RESET, YELLOW.to_string() + &tag + &RESET);
                read_func(children[i] as *mut mpc_ast_t);
            },
            "module" =>
            {
                println!("{}: {}", MAGENTA.to_string() + &"tag", YELLOW.to_string() + &tag + &RESET);
                println!("{} {}:", GREEN.to_string() + &"module" + &RESET,
                        CStr::from_ptr( (*child.get_children()[0]).contents ).to_string_lossy().into_owned());
            },
            "statement" => println!("statement"),
            _ => {},
        }
        process(children[i] as *mut mpc_ast_t, level + 1);
    }

}

pub unsafe fn make_string(s: String) -> *mut c_char
{
    CString::new(s).unwrap().into_raw()
}

pub unsafe fn read_string(s: *mut c_char) -> String
{
    CStr::from_ptr(s).to_string_lossy().into_owned()
}

pub unsafe fn read_func(ast_ptr: *mut mpc_ast_t)
{

}
<|endoftext|>