<commit_before>use conf::Conf;
use error::{Result, ResultContext};
use files;
use tera::{Tera, Context};
use post::{Post, PostKind};
use std::{env, fs};
use std::path::PathBuf;

#[derive(Debug)]
pub struct Site {
    ctx: Context,
    articles: Vec<Post>,
    pages: Vec<Post>,
    tera: Tera,
}

impl Site {
    pub fn collect(conf: &Conf) -> Result<Self> {
        let in_dir = conf.in_dir.as_ref()
            .map(PathBuf::from)
            .unwrap_or(env::current_dir()?);

        let mut articles = vec![];
        let mut pages = vec![];

        let template_path = in_dir.join(::THEME_DIR).join("*");

        let mut tera = Tera::new(template_path.to_str().unwrap())
            .context("compile templates fails".to_string())?;

        tera.autoescape_on(vec![]);

        for entry in fs::read_dir(in_dir.join(::SRC_DIR))? {
            let post = match Post::from_file(&entry?.path())? {
                None => continue,
                Some(p) => p,
            };
            match post.kind {
                PostKind::Article => articles.push(post),
                PostKind::Page => pages.push(post),
            }
        }
        articles.sort_by(|x, y| y.ts.cmp(&x.ts));
        pages.sort_by(|x, y| y.ts.cmp(&x.ts));

        let mut ctx = Context::new();
        ctx.add("articles", &articles);
        ctx.add("pages", &pages);
        ctx.add("conf", &conf);
        if let Some(p) = articles.first() {
            ctx.add("latest_article", p);
        }

        Ok(Site { ctx, articles, pages, tera })
    }

    pub fn generate(&self, conf: &Conf) -> Result<()> {
        let out_dir = conf.out_dir.as_ref()
            .map(PathBuf::from)
            .unwrap_or(env::current_dir()?);

        for p in self.articles.iter().chain(self.pages.iter()) {
            // skip the leading slash to make the output path correct
            let url = &p.url()?[1..];

            let mut ctx = Context::new();
            ctx.extend(self.ctx.clone());
            ctx.add("post", p);

            let rendered = self.tera.render(::POST_FILE, &ctx)
                .context(format!("fail to generate {}", url))?;
            println!("generating {}", url);
            files::fwrite(out_dir.join(url), rendered.as_bytes(),
                          conf.force.unwrap_or(false))?;
        }

        for f in &[::INDEX_FILE, ::ARCHIVE_FILE] {
            println!("generating {}", f);
            self.tera.render(f, &self.ctx)
                .context(format!("fail to generate {}", f))
                .and_then(|rendered| files::fwrite(out_dir.join(f), rendered.as_bytes(),
                                                   conf.force.unwrap_or(false)))?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::std::{env, fs};
    use ::post::{create_post, PostKind};

    #[test]
    fn test_generate() {
        let dir = env::temp_dir().join("site");
        ::new::create_site(dir.clone(), true).unwrap();

        let mut c = Conf::default();
        c.force = Some(true);
        c.in_dir = Some(dir.to_str().unwrap().to_string());
        c.out_dir = Some(dir.to_str().unwrap().to_string());

        create_post(dir.join(::SRC_DIR).join("a.md"), c.clone(), PostKind::Article).unwrap();
        create_post(dir.join(::SRC_DIR).join("p.md"), c.clone(), PostKind::Page).unwrap();

        let site = Site::collect(&c).unwrap();

        assert!(site.articles.first().unwrap().link == "a");
        assert!(site.pages.first().unwrap().link == "p");

        site.generate(&c).unwrap();

        let p = site.articles.first().unwrap();
        assert!(dir.join("p.html").exists());
        assert!(dir.join(p.ts.format("%Y/%m/%d").to_string()).join("a.html").exists());
        assert!(dir.join(::INDEX_FILE).exists());
        assert!(dir.join(::ARCHIVE_FILE).exists());

        fs::remove_dir_all(dir).unwrap();
    }
}
<commit_msg>Do not print when generating site<commit_after>use conf::Conf;
use error::{Result, ResultContext};
use files;
use tera::{Tera, Context};
use post::{Post, PostKind};
use std::{env, fs};
use std::path::PathBuf;

#[derive(Debug)]
pub struct Site {
    ctx: Context,
    articles: Vec<Post>,
    pages: Vec<Post>,
    tera: Tera,
}

impl Site {
    pub fn collect(conf: &Conf) -> Result<Self> {
        let in_dir = conf.in_dir.as_ref()
            .map(PathBuf::from)
            .unwrap_or(env::current_dir()?);

        let mut articles = vec![];
        let mut pages = vec![];

        let template_path = in_dir.join(::THEME_DIR).join("*");

        let mut tera = Tera::new(template_path.to_str().unwrap())
            .context("compile templates fails".to_string())?;

        tera.autoescape_on(vec![]);

        for entry in fs::read_dir(in_dir.join(::SRC_DIR))? {
            let post = match Post::from_file(&entry?.path())? {
                None => continue,
                Some(p) => p,
            };
            match post.kind {
                PostKind::Article => articles.push(post),
                PostKind::Page => pages.push(post),
            }
        }
        articles.sort_by(|x, y| y.ts.cmp(&x.ts));
        pages.sort_by(|x, y| y.ts.cmp(&x.ts));

        let mut ctx = Context::new();
        ctx.add("articles", &articles);
        ctx.add("pages", &pages);
        ctx.add("conf", &conf);
        if let Some(p) = articles.first() {
            ctx.add("latest_article", p);
        }

        Ok(Site { ctx, articles, pages, tera })
    }

    pub fn generate(&self, conf: &Conf) -> Result<()> {
        let out_dir = conf.out_dir.as_ref()
            .map(PathBuf::from)
            .unwrap_or(env::current_dir()?);

        for p in self.articles.iter().chain(self.pages.iter()) {
            // skip the leading slash to make the output path correct
            let url = &p.url()?[1..];

            let mut ctx = Context::new();
            ctx.extend(self.ctx.clone());
            ctx.add("post", p);

            let rendered = self.tera.render(::POST_FILE, &ctx)
                .context(format!("fail to generate {}", url))?;
            files::fwrite(out_dir.join(url), rendered.as_bytes(),
                          conf.force.unwrap_or(false))?;
        }

        for f in &[::INDEX_FILE, ::ARCHIVE_FILE] {
            self.tera.render(f, &self.ctx)
                .context(format!("fail to generate {}", f))
                .and_then(|rendered| files::fwrite(out_dir.join(f), rendered.as_bytes(),
                                                   conf.force.unwrap_or(false)))?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ::std::{env, fs};
    use ::post::{create_post, PostKind};

    #[test]
    fn test_generate() {
        let dir = env::temp_dir().join("site");
        ::new::create_site(dir.clone(), true).unwrap();

        let mut c = Conf::default();
        c.force = Some(true);
        c.in_dir = Some(dir.to_str().unwrap().to_string());
        c.out_dir = Some(dir.to_str().unwrap().to_string());

        create_post(dir.join(::SRC_DIR).join("a.md"), c.clone(), PostKind::Article).unwrap();
        create_post(dir.join(::SRC_DIR).join("p.md"), c.clone(), PostKind::Page).unwrap();

        let site = Site::collect(&c).unwrap();

        assert!(site.articles.first().unwrap().link == "a");
        assert!(site.pages.first().unwrap().link == "p");

        site.generate(&c).unwrap();

        let p = site.articles.first().unwrap();
        assert!(dir.join("p.html").exists());
        assert!(dir.join(p.ts.format("%Y/%m/%d").to_string()).join("a.html").exists());
        assert!(dir.join(::INDEX_FILE).exists());
        assert!(dir.join(::ARCHIVE_FILE).exists());

        fs::remove_dir_all(dir).unwrap();
    }
}
<|endoftext|>


<commit_before>use crate::config::{Config, FtpProtocol};
use crate::ftp::{Ftp, FtpOperations, Sftp};
use crate::{comp_as_str, sha1dir};
use crate::{filter, last_path_comp_as_str};

use anyhow::{anyhow, Context, Result};
use std::collections::BTreeMap;
use std::fs;
use std::io::Read;
use std::path::{Component, Path, PathBuf};
use walkdir::WalkDir;

static CRC_FILE_NAME: &str = "checksums.crc";
static OLD_CRC_FILE_NAME: &str = "checksums_old.crc";

pub struct Synchronizer {
    server: String,
    ftp_target_dir: PathBuf,
    output_path: PathBuf,
    output_path_offset: usize,
    ftp_stream: Box<dyn FtpOperations>,
}

impl Synchronizer {
    pub fn new(conf: &Config) -> Result<Synchronizer> {
        let output_path = PathBuf::from(
            conf.output_dir
                .as_ref()
                .context("check 'config.toml' output_dir is invalid")?,
        );
        let sync_settings = conf
            .sync_settings
            .as_ref()
            .context("'config.toml' sync settings not found")?;
        let server = String::from(&sync_settings.ftp_server);
        let user = &sync_settings.ftp_user;
        let port = sync_settings.ftp_port.unwrap_or(21);
        let protocol = sync_settings.ftp_protocol.unwrap_or(FtpProtocol::Ftp);
        let ftp_stream: Box<dyn FtpOperations> = if protocol == FtpProtocol::Ftp {
            Box::new(Ftp::new(&server, port, user)?)
        } else {
            Box::new(Sftp::new(&server, port, user)?)
        };
        Ok(Synchronizer {
            server,
            ftp_target_dir: PathBuf::from(sync_settings.ftp_target_dir.as_deref().unwrap_or("/")),
            output_path_offset: output_path.as_path().components().count(),
            output_path,
            ftp_stream,
        })
    }

    pub fn execute(&mut self) -> Result<()> {
        let checksums = self.create_checksums_file()?;

        // first try to push delta first
        if let Err(ref err) = self.push_delta(&checksums) {
            println!("Couldn't push delta '{err}'");
            log::info!("Couldn't push delta '{err}'");
            self.push_all_files()?;
        }
        let crc_file_path = self.output_path.join(CRC_FILE_NAME);
        self.ftp_push_file(&crc_file_path)?;
        Ok(())
    }

    pub fn push_all_files(&mut self) -> Result<()> {
        println!("Push all files to FTP server {}", &self.server);
        log::info!("Push all files to FTP server {}", &self.server);
        let walker = WalkDir::new(self.output_path.as_path())
            .min_depth(1)
            .into_iter();
        for entry in walker {
            let entry = entry.unwrap();
            if filter::is_directory(&entry) {
                self.ftp_create_and_goto_dir(entry.path())?;
            } else {
                self.ftp_push_file(entry.path())?;
            }
        }
        Ok(())
    }

    fn create_checksums_file(&self) -> Result<BTreeMap<PathBuf, [u8; 20]>> {
        let hash_file_path = PathBuf::from(&self.output_path).join(CRC_FILE_NAME);
        let _ = std::fs::remove_file(&hash_file_path); // ignore if checksums did not exist before
        let checksums = sha1dir::create_hashes(&self.output_path)?;
        let f = std::fs::File::create(&hash_file_path).expect("Unable to create file");
        serde_json::to_writer(f, &checksums).expect("Unable to write data");
        Ok(checksums)
    }

    fn retrieve_checksums_file(&mut self) -> Result<PathBuf> {
        let mut hash_file_path = self.ftp_goto_target_dir()?;
        hash_file_path.push(CRC_FILE_NAME);
        let old_hash_file_path = PathBuf::from(&self.output_path).join(OLD_CRC_FILE_NAME);
        self.ftp_stream
            .get(&hash_file_path, &old_hash_file_path)
            .context("loading of existing checksums file failed")?;
        Ok(old_hash_file_path)
    }

    fn push_delta(&mut self, checksums: &BTreeMap<PathBuf, [u8; 20]>) -> Result<()> {
        // load old checksums
        let old_hash_file_path = self.retrieve_checksums_file()?;
        let mut data = String::new();
        std::fs::File::open(&old_hash_file_path)
            .and_then(|mut f| f.read_to_string(&mut data))
            .context("couldn't find or read file 'config.toml'")?;
        let mut old_checksums: BTreeMap<PathBuf, [u8; 20]> = serde_json::from_str(&data)?;

        // ignore if old CRC file could not be deleted
        let _ = fs::remove_file(&old_hash_file_path);

        // check for deltas
        let mut parent_dir: Option<&Path> = None;
        let mut new_dir_found = false;
        for (new_path, new_hash) in checksums.iter() {
            // copy new directory
            if new_dir_found && new_path.starts_with(parent_dir.unwrap()) {
                log::info!("Created: {new_path:?}");
                if new_path.is_dir() {
                    self.ftp_create_and_goto_dir(new_path)?;
                } else {
                    self.ftp_push_file(new_path)?;
                }
                continue;
            } else {
                new_dir_found = false;
            }

            // skip unchanged entries
            if let Some(d) = parent_dir {
                if new_path.starts_with(d) {
                    log::info!("Unchanged: {new_path:?}");
                    old_checksums.remove(new_path);
                    continue;
                } else {
                    parent_dir = None;
                }
            }

            if let Some(old_hash) = old_checksums.remove(new_path) {
                // entry exists on remote check wether update is necessary
                let hash_is_equal = &old_hash == new_hash;
                if hash_is_equal {
                    log::info!("Unchanged: {new_path:?}");
                    if new_path.is_dir() {
                        parent_dir = Some(new_path);
                    } else {
                        continue;
                    }
                } else {
                    log::info!("Updated: {new_path:?}");
                    if new_path.is_dir() {
                        self.ftp_create_and_goto_dir(new_path)?;
                    } else {
                        self.ftp_push_file(new_path)?;
                    }
                }
            } else {
                // entry should not exist on remote but maybe the path still exists
                // therefore delete file or directory to be sure!
                if self.ftp_remove_file(new_path).is_ok() {
                    log::error!("Removed?: {new_path:?} - maybe changed file into dir?");
                } else if self.ftp_remove_dir(new_path).is_ok() {
                    log::error!("Removed?: {new_path:?} - maybe changed dir to filename?");
                }
                // now we can safely update the remote
                log::info!("Created: {new_path:?}");
                if new_path.is_dir() {
                    self.ftp_create_and_goto_dir(new_path)?;
                    parent_dir = Some(new_path);
                    new_dir_found = true;
                } else {
                    self.ftp_push_file(new_path)?;
                }
            }
        }

        // finally delete removed entries from remote
        parent_dir = None;
        for old_path in old_checksums.keys() {
            log::info!("removed {old_path:?}");
            if self.ftp_is_dir(old_path) {
                if let Some(del_dir) = parent_dir {
                    self.ftp_remove_dir(del_dir)?
                }
                parent_dir = Some(old_path);
            } else {
                self.ftp_remove_file(old_path)?;
            }
        }
        if let Some(del_dir) = parent_dir {
            self.ftp_remove_dir(del_dir)?
        }

        Ok(())
    }

    fn ftp_goto_target_dir(&mut self) -> Result<PathBuf> {
        self.ftp_stream.cwdroot()?;
        let mut to_dir = PathBuf::new();
        for comp in self
            .ftp_target_dir
            .components()
            .skip_while(|c| matches!(*c, Component::Prefix(_) | Component::RootDir))
        {
            let new_dir = comp_as_str!(comp)?;
            to_dir.push(new_dir);
            if self.ftp_stream.cwd(&to_dir).is_err() {
                log::info!("Creating '{new_dir}'");
                self.ftp_stream.mkdir(&to_dir)?;
                self.ftp_stream.cwd(&to_dir)?;
            }
        }
        Ok(to_dir)
    }

    fn ftp_create_and_goto_dir(&mut self, dir: &Path) -> Result<PathBuf> {
        let mut to_dir = self.ftp_goto_target_dir()?;
        for comp in dir.components().skip(if dir.starts_with(".") {
            1
        } else {
            self.output_path_offset
        }) {
            let new_dir = comp_as_str!(comp)?;
            to_dir.push(new_dir);
            if self.ftp_stream.cwd(&to_dir).is_err() {
                log::info!("Creating '{new_dir}'");
                self.ftp_stream.mkdir(&to_dir)?;
                self.ftp_stream.cwd(&to_dir)?;
            }
        }
        Ok(to_dir)
    }

    fn ftp_goto_parent_dir(&mut self, src: &Path) -> Result<PathBuf> {
        let mut parent_dir = src.to_path_buf();
        if !parent_dir.pop() {
            return Err(anyhow!(
                "Internal error: couldn't handle path {:?}",
                parent_dir
            ));
        }
        self.ftp_goto_dir(parent_dir.as_path())
    }

    fn ftp_goto_dir(&mut self, src: &Path) -> Result<PathBuf> {
        let mut path = self.ftp_goto_target_dir()?;
        for comp in src.components().skip(if src.starts_with(".") {
            1
        } else {
            self.output_path_offset
        }) {
            path.push(comp_as_str!(comp)?);
            self.ftp_stream.cwd(&path)?;
        }
        Ok(path)
    }

    fn ftp_remove_dir(&mut self, dir: &Path) -> Result<()> {
        let mut src_dir = self.ftp_goto_parent_dir(dir)?;
        src_dir.push(last_path_comp_as_str!(dir)?);
        self.ftp_stream.rmdir(&src_dir)?;
        Ok(())
    }

    fn ftp_remove_file(&mut self, file: &Path) -> Result<()> {
        let mut src_dir = self.ftp_goto_parent_dir(file)?;
        src_dir.push(last_path_comp_as_str!(file)?);
        self.ftp_stream.del(&src_dir)?;
        Ok(())
    }

    fn ftp_push_file(&mut self, file: &Path) -> Result<()> {
        let mut to_dir = self.ftp_create_and_goto_dir(file.parent().unwrap())?;
        let file_name = file.file_name().and_then(|s| s.to_str()).unwrap();
        to_dir.push(file_name);
        self.ftp_stream.put(&to_dir, file)
    }

    fn ftp_is_dir(&mut self, path: &Path) -> bool {
        self.ftp_goto_dir(path).is_ok()
    }
}
<commit_msg>choose correct default port depending on the choosen sync protocol FTP = 21 and SFTP = 22<commit_after>use crate::config::{Config, FtpProtocol};
use crate::ftp::{Ftp, FtpOperations, Sftp};
use crate::{comp_as_str, sha1dir};
use crate::{filter, last_path_comp_as_str};

use anyhow::{anyhow, Context, Result};
use std::collections::BTreeMap;
use std::fs;
use std::io::Read;
use std::path::{Component, Path, PathBuf};
use walkdir::WalkDir;

static CRC_FILE_NAME: &str = "checksums.crc";
static OLD_CRC_FILE_NAME: &str = "checksums_old.crc";

pub struct Synchronizer {
    server: String,
    ftp_target_dir: PathBuf,
    output_path: PathBuf,
    output_path_offset: usize,
    ftp_stream: Box<dyn FtpOperations>,
}

impl Synchronizer {
    pub fn new(conf: &Config) -> Result<Synchronizer> {
        let output_path = PathBuf::from(
            conf.output_dir
                .as_ref()
                .context("check 'config.toml' output_dir is invalid")?,
        );
        let sync_settings = conf
            .sync_settings
            .as_ref()
            .context("'config.toml' sync settings not found")?;
        let server = String::from(&sync_settings.ftp_server);
        let user = &sync_settings.ftp_user;
        let protocol = sync_settings.ftp_protocol.unwrap_or(FtpProtocol::Ftp);
        let port = sync_settings
            .ftp_port
            .unwrap_or(if protocol == FtpProtocol::Ftp { 21 } else { 22 });
        let ftp_stream: Box<dyn FtpOperations> = if protocol == FtpProtocol::Ftp {
            Box::new(Ftp::new(&server, port, user)?)
        } else {
            Box::new(Sftp::new(&server, port, user)?)
        };
        Ok(Synchronizer {
            server,
            ftp_target_dir: PathBuf::from(sync_settings.ftp_target_dir.as_deref().unwrap_or("/")),
            output_path_offset: output_path.as_path().components().count(),
            output_path,
            ftp_stream,
        })
    }

    pub fn execute(&mut self) -> Result<()> {
        let checksums = self.create_checksums_file()?;

        // first try to push delta first
        if let Err(ref err) = self.push_delta(&checksums) {
            println!("Couldn't push delta '{err}'");
            log::info!("Couldn't push delta '{err}'");
            self.push_all_files()?;
        }
        let crc_file_path = self.output_path.join(CRC_FILE_NAME);
        self.ftp_push_file(&crc_file_path)?;
        Ok(())
    }

    pub fn push_all_files(&mut self) -> Result<()> {
        println!("Push all files to FTP server {}", &self.server);
        log::info!("Push all files to FTP server {}", &self.server);
        let walker = WalkDir::new(self.output_path.as_path())
            .min_depth(1)
            .into_iter();
        for entry in walker {
            let entry = entry.unwrap();
            if filter::is_directory(&entry) {
                self.ftp_create_and_goto_dir(entry.path())?;
            } else {
                self.ftp_push_file(entry.path())?;
            }
        }
        Ok(())
    }

    fn create_checksums_file(&self) -> Result<BTreeMap<PathBuf, [u8; 20]>> {
        let hash_file_path = PathBuf::from(&self.output_path).join(CRC_FILE_NAME);
        let _ = std::fs::remove_file(&hash_file_path); // ignore if checksums did not exist before
        let checksums = sha1dir::create_hashes(&self.output_path)?;
        let f = std::fs::File::create(&hash_file_path).expect("Unable to create file");
        serde_json::to_writer(f, &checksums).expect("Unable to write data");
        Ok(checksums)
    }

    fn retrieve_checksums_file(&mut self) -> Result<PathBuf> {
        let mut hash_file_path = self.ftp_goto_target_dir()?;
        hash_file_path.push(CRC_FILE_NAME);
        let old_hash_file_path = PathBuf::from(&self.output_path).join(OLD_CRC_FILE_NAME);
        self.ftp_stream
            .get(&hash_file_path, &old_hash_file_path)
            .context("loading of existing checksums file failed")?;
        Ok(old_hash_file_path)
    }

    fn push_delta(&mut self, checksums: &BTreeMap<PathBuf, [u8; 20]>) -> Result<()> {
        // load old checksums
        let old_hash_file_path = self.retrieve_checksums_file()?;
        let mut data = String::new();
        std::fs::File::open(&old_hash_file_path)
            .and_then(|mut f| f.read_to_string(&mut data))
            .context("couldn't find or read file 'config.toml'")?;
        let mut old_checksums: BTreeMap<PathBuf, [u8; 20]> = serde_json::from_str(&data)?;

        // ignore if old CRC file could not be deleted
        let _ = fs::remove_file(&old_hash_file_path);

        // check for deltas
        let mut parent_dir: Option<&Path> = None;
        let mut new_dir_found = false;
        for (new_path, new_hash) in checksums.iter() {
            // copy new directory
            if new_dir_found && new_path.starts_with(parent_dir.unwrap()) {
                log::info!("Created: {new_path:?}");
                if new_path.is_dir() {
                    self.ftp_create_and_goto_dir(new_path)?;
                } else {
                    self.ftp_push_file(new_path)?;
                }
                continue;
            } else {
                new_dir_found = false;
            }

            // skip unchanged entries
            if let Some(d) = parent_dir {
                if new_path.starts_with(d) {
                    log::info!("Unchanged: {new_path:?}");
                    old_checksums.remove(new_path);
                    continue;
                } else {
                    parent_dir = None;
                }
            }

            if let Some(old_hash) = old_checksums.remove(new_path) {
                // entry exists on remote check wether update is necessary
                let hash_is_equal = &old_hash == new_hash;
                if hash_is_equal {
                    log::info!("Unchanged: {new_path:?}");
                    if new_path.is_dir() {
                        parent_dir = Some(new_path);
                    } else {
                        continue;
                    }
                } else {
                    log::info!("Updated: {new_path:?}");
                    if new_path.is_dir() {
                        self.ftp_create_and_goto_dir(new_path)?;
                    } else {
                        self.ftp_push_file(new_path)?;
                    }
                }
            } else {
                // entry should not exist on remote but maybe the path still exists
                // therefore delete file or directory to be sure!
                if self.ftp_remove_file(new_path).is_ok() {
                    log::error!("Removed?: {new_path:?} - maybe changed file into dir?");
                } else if self.ftp_remove_dir(new_path).is_ok() {
                    log::error!("Removed?: {new_path:?} - maybe changed dir to filename?");
                }
                // now we can safely update the remote
                log::info!("Created: {new_path:?}");
                if new_path.is_dir() {
                    self.ftp_create_and_goto_dir(new_path)?;
                    parent_dir = Some(new_path);
                    new_dir_found = true;
                } else {
                    self.ftp_push_file(new_path)?;
                }
            }
        }

        // finally delete removed entries from remote
        parent_dir = None;
        for old_path in old_checksums.keys() {
            log::info!("removed {old_path:?}");
            if self.ftp_is_dir(old_path) {
                if let Some(del_dir) = parent_dir {
                    self.ftp_remove_dir(del_dir)?
                }
                parent_dir = Some(old_path);
            } else {
                self.ftp_remove_file(old_path)?;
            }
        }
        if let Some(del_dir) = parent_dir {
            self.ftp_remove_dir(del_dir)?
        }

        Ok(())
    }

    fn ftp_goto_target_dir(&mut self) -> Result<PathBuf> {
        self.ftp_stream.cwdroot()?;
        let mut to_dir = PathBuf::new();
        for comp in self
            .ftp_target_dir
            .components()
            .skip_while(|c| matches!(*c, Component::Prefix(_) | Component::RootDir))
        {
            let new_dir = comp_as_str!(comp)?;
            to_dir.push(new_dir);
            if self.ftp_stream.cwd(&to_dir).is_err() {
                log::info!("Creating '{new_dir}'");
                self.ftp_stream.mkdir(&to_dir)?;
                self.ftp_stream.cwd(&to_dir)?;
            }
        }
        Ok(to_dir)
    }

    fn ftp_create_and_goto_dir(&mut self, dir: &Path) -> Result<PathBuf> {
        let mut to_dir = self.ftp_goto_target_dir()?;
        for comp in dir.components().skip(if dir.starts_with(".") {
            1
        } else {
            self.output_path_offset
        }) {
            let new_dir = comp_as_str!(comp)?;
            to_dir.push(new_dir);
            if self.ftp_stream.cwd(&to_dir).is_err() {
                log::info!("Creating '{new_dir}'");
                self.ftp_stream.mkdir(&to_dir)?;
                self.ftp_stream.cwd(&to_dir)?;
            }
        }
        Ok(to_dir)
    }

    fn ftp_goto_parent_dir(&mut self, src: &Path) -> Result<PathBuf> {
        let mut parent_dir = src.to_path_buf();
        if !parent_dir.pop() {
            return Err(anyhow!(
                "Internal error: couldn't handle path {:?}",
                parent_dir
            ));
        }
        self.ftp_goto_dir(parent_dir.as_path())
    }

    fn ftp_goto_dir(&mut self, src: &Path) -> Result<PathBuf> {
        let mut path = self.ftp_goto_target_dir()?;
        for comp in src.components().skip(if src.starts_with(".") {
            1
        } else {
            self.output_path_offset
        }) {
            path.push(comp_as_str!(comp)?);
            self.ftp_stream.cwd(&path)?;
        }
        Ok(path)
    }

    fn ftp_remove_dir(&mut self, dir: &Path) -> Result<()> {
        let mut src_dir = self.ftp_goto_parent_dir(dir)?;
        src_dir.push(last_path_comp_as_str!(dir)?);
        self.ftp_stream.rmdir(&src_dir)?;
        Ok(())
    }

    fn ftp_remove_file(&mut self, file: &Path) -> Result<()> {
        let mut src_dir = self.ftp_goto_parent_dir(file)?;
        src_dir.push(last_path_comp_as_str!(file)?);
        self.ftp_stream.del(&src_dir)?;
        Ok(())
    }

    fn ftp_push_file(&mut self, file: &Path) -> Result<()> {
        let mut to_dir = self.ftp_create_and_goto_dir(file.parent().unwrap())?;
        let file_name = file.file_name().and_then(|s| s.to_str()).unwrap();
        to_dir.push(file_name);
        self.ftp_stream.put(&to_dir, file)
    }

    fn ftp_is_dir(&mut self, path: &Path) -> bool {
        self.ftp_goto_dir(path).is_ok()
    }
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::fmt;
use std::ops::{Add, AddAssign, Deref};
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};

use super::content::{Content, ToContent};

#[derive(Debug, Default)]
pub struct Text {
    inner: Option<String>,
    text: Vec<Content>,
}

impl<T> From<T> for Text
    where T: ToContent<T>
{
    fn from(s: T) -> Self {
        Text {
            inner: None,
            text: vec![Content::from(s)],
        }
    }
}

impl Text {
    pub fn new() -> Self {
        Text::default()
    }

    pub fn len(&self) -> usize {
        let mut len: usize = 0;
        for elt in &self.text {
            len += elt.len();
        }
        len
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl fmt::Display for Text {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad(&format!("{:?}", &self.text))
    }
}

impl Deref for Text {
    type Target = OsStr;
    fn deref(&self) -> &OsStr {
        if self.is_empty() {
            OsStr::new("")
        } else {
            match self.text[0] {
                Content::String(ref s) => s.as_ref(),
                Content::OsString(ref s) => s,
                Content::CString(ref s) => OsStr::from_bytes(s.as_bytes()),
                Content::PathBuf(ref p) => p.as_ref(),
            }
        }
    }
}

// Doesn't work - conflicting Deref implementation

// impl Deref for Text {
//     type Target = Path;
//     fn deref(&self) -> &Path {
//         Path::new(&self.text)
//     }
// }

// Add

impl Add for Text {
    type Output = Text;
    fn add(self, rhs: Text) -> Self::Output {
        let mut text = self.text.clone();
        text.extend_from_slice(&rhs.text);
        Text { text: text }
    }
}

impl<T> Add<T> for Text
    where T: ToContent<T>
{
    type Output = Text;
    fn add(self, rhs: T) -> Self::Output {
        let mut text = self.text.clone();
        text.push(Content::from(rhs));
        Text { text: text }
    }
}

// AddAssign implementation

impl AddAssign for Text {
    fn add_assign(&mut self, other: Self) {
        self.text.extend_from_slice(&other.text)
    }
}

// PartialEq implementation

// impl<T: Borrow<OsStr> + ?Sized> PartialEq<T> for Text {
//     default fn eq(&self, other: &T) -> bool {
//         unimplemented!()
//     }
// }

// impl<T: Borrow<Path> + ?Sized> PartialEq<T> for Text {
//     default fn eq(&self, other: &T) -> bool {
//         unimplemented!()
//     }
// }

impl PartialEq<str> for Text {
    fn eq(&self, other: &str) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<&'a str> for Text {
    fn eq(&self, other: &&'a str) -> bool {
        unimplemented!()
    }
}

impl PartialEq<String> for Text {
    fn eq(&self, other: &String) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<String> for &'a Text {
    fn eq(&self, other: &String) -> bool {
        unimplemented!()
        // self.text == *other
    }
}

impl PartialEq<Path> for Text {
    fn eq(&self, other: &Path) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<&'a Path> for Text {
    fn eq(&self, other: &&'a Path) -> bool {
        unimplemented!()
    }
}

impl PartialEq<PathBuf> for Text {
    fn eq(&self, other: &PathBuf) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<PathBuf> for &'a Text {
    fn eq(&self, other: &PathBuf) -> bool {
        unimplemented!()
    }
}

// impl<T> From<T> for Text
//     where T: Textable
// {
//     fn from(s: T) -> Self {
//         s.to_text()
//     }
// }

impl From<Text> for String {
    fn from(t: Text) -> Self {
        unimplemented!()
    }
}

impl<'a> From<&'a Text> for &'a Path {
    fn from(t: &'a Text) -> Self {
        unimplemented!()
    }
}
<commit_msg>Revert "Add internal representation"<commit_after>use std::convert::From;
use std::fmt;
use std::ops::{Add, AddAssign, Deref};
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};

use super::content::{Content, ToContent};

#[derive(Debug, Default)]
pub struct Text {
    text: Vec<Content>,
}

impl<T> From<T> for Text
    where T: ToContent<T>
{
    fn from(s: T) -> Self {
        Text { text: vec![Content::from(s)] }
    }
}

impl Text {
    pub fn new() -> Self {
        Text::default()
    }

    pub fn len(&self) -> usize {
        let mut len: usize = 0;
        for elt in &self.text {
            len += elt.len();
        }
        len
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl fmt::Display for Text {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad(&format!("{:?}", &self.text))
    }
}

impl Deref for Text {
    type Target = OsStr;
    fn deref(&self) -> &OsStr {
        if self.is_empty() {
            OsStr::new("")
        } else {
            match self.text[0] {
                Content::String(ref s) => s.as_ref(),
                Content::OsString(ref s) => s,
                Content::CString(ref s) => OsStr::from_bytes(s.as_bytes()),
                Content::PathBuf(ref p) => p.as_ref(),
            }
        }
    }
}

// Doesn't work - conflicting Deref implementation

// impl Deref for Text {
//     type Target = Path;
//     fn deref(&self) -> &Path {
//         Path::new(&self.text)
//     }
// }

// Add

impl Add for Text {
    type Output = Text;
    fn add(self, rhs: Text) -> Self::Output {
        let mut text = self.text.clone();
        text.extend_from_slice(&rhs.text);
        Text { text: text }
    }
}

impl<T> Add<T> for Text
    where T: ToContent<T>
{
    type Output = Text;
    fn add(self, rhs: T) -> Self::Output {
        let mut text = self.text.clone();
        text.push(Content::from(rhs));
        Text { text: text }
    }
}

// AddAssign implementation

impl AddAssign for Text {
    fn add_assign(&mut self, other: Self) {
        self.text.extend_from_slice(&other.text)
    }
}

// PartialEq implementation

// impl<T: Borrow<OsStr> + ?Sized> PartialEq<T> for Text {
//     default fn eq(&self, other: &T) -> bool {
//         unimplemented!()
//     }
// }

// impl<T: Borrow<Path> + ?Sized> PartialEq<T> for Text {
//     default fn eq(&self, other: &T) -> bool {
//         unimplemented!()
//     }
// }

impl PartialEq<str> for Text {
    fn eq(&self, other: &str) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<&'a str> for Text {
    fn eq(&self, other: &&'a str) -> bool {
        unimplemented!()
    }
}

impl PartialEq<String> for Text {
    fn eq(&self, other: &String) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<String> for &'a Text {
    fn eq(&self, other: &String) -> bool {
        unimplemented!()
        // self.text == *other
    }
}

impl PartialEq<Path> for Text {
    fn eq(&self, other: &Path) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<&'a Path> for Text {
    fn eq(&self, other: &&'a Path) -> bool {
        unimplemented!()
    }
}

impl PartialEq<PathBuf> for Text {
    fn eq(&self, other: &PathBuf) -> bool {
        unimplemented!()
    }
}

impl<'a> PartialEq<PathBuf> for &'a Text {
    fn eq(&self, other: &PathBuf) -> bool {
        unimplemented!()
    }
}

// impl<T> From<T> for Text
//     where T: Textable
// {
//     fn from(s: T) -> Self {
//         s.to_text()
//     }
// }

impl From<Text> for String {
    fn from(t: Text) -> Self {
        unimplemented!()
    }
}

impl<'a> From<&'a Text> for &'a Path {
    fn from(t: &'a Text) -> Self {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>//! Functions to read time stamp counters on x86.

use crate::arch::{__rdtscp, _rdtsc};

/// Read the time stamp counter.
///
/// The RDTSC instruction is not a serializing instruction.
/// It does not necessarily wait until all previous instructions
/// have been executed before reading the counter. Similarly,
/// subsequent instructions may begin execution before the
/// read operation is performed. If software requires RDTSC to be
/// executed only after all previous instructions have completed locally,
/// it can either use RDTSCP or execute the sequence LFENCE;RDTSC.
///
/// # Safety
/// * Causes a GP fault if the TSD flag in register CR4 is set and the CPL
///   is greater than 0.
pub unsafe fn rdtsc() -> u64 {
    _rdtsc() as u64
}

/// Read the time stamp counter.
///
/// The RDTSCP instruction waits until all previous instructions
/// have been executed before reading the counter.
/// However, subsequent instructions may begin execution
/// before the read operation is performed.
///
/// Volatile is used here because the function may be used to act as
/// an instruction barrier.
///
/// # Safety
/// * Causes a GP fault if the TSD flag in register CR4 is set and the
///   CPL is greater than 0.
pub unsafe fn rdtscp() -> u64 {
    let mut _aux = 0;
    __rdtscp(&mut _aux)
}

#[cfg(all(test, feature = "utest"))]
mod test {
    use super::*;

    #[test]
    fn check_rdtsc() {
        let cpuid = crate::cpuid::CpuId::new();
        let has_tsc = cpuid
            .get_feature_info()
            .map_or(false, |finfo| finfo.has_tsc());

        if has_tsc {
            unsafe {
                assert!(rdtsc() > 0, "rdtsc returned 0, unlikely!");
            }
        }
    }

    #[test]
    fn check_rdtscp() {
        let cpuid = crate::cpuid::CpuId::new();
        let has_tsc = cpuid
            .get_feature_info()
            .map_or(false, |finfo| finfo.has_tsc());

        if has_tsc {
            unsafe {
                assert!(rdtscp() > 0, "rdtscp returned 0, unlikely!");
            }
        }
    }
}
<commit_msg>Check correctly for rdtscp.<commit_after>//! Functions to read time stamp counters on x86.

use crate::arch::{__rdtscp, _rdtsc};

/// Read the time stamp counter.
///
/// The RDTSC instruction is not a serializing instruction.
/// It does not necessarily wait until all previous instructions
/// have been executed before reading the counter. Similarly,
/// subsequent instructions may begin execution before the
/// read operation is performed. If software requires RDTSC to be
/// executed only after all previous instructions have completed locally,
/// it can either use RDTSCP or execute the sequence LFENCE;RDTSC.
///
/// # Safety
/// * Causes a GP fault if the TSD flag in register CR4 is set and the CPL
///   is greater than 0.
pub unsafe fn rdtsc() -> u64 {
    _rdtsc() as u64
}

/// Read the time stamp counter.
///
/// The RDTSCP instruction waits until all previous instructions
/// have been executed before reading the counter.
/// However, subsequent instructions may begin execution
/// before the read operation is performed.
///
/// Volatile is used here because the function may be used to act as
/// an instruction barrier.
///
/// # Safety
/// * Causes a GP fault if the TSD flag in register CR4 is set and the
///   CPL is greater than 0.
pub unsafe fn rdtscp() -> u64 {
    let mut _aux = 0;
    __rdtscp(&mut _aux)
}

#[cfg(all(test, feature = "utest"))]
mod test {
    use super::*;

    #[test]
    fn check_rdtsc() {
        let cpuid = crate::cpuid::CpuId::new();
        let has_tsc = cpuid
            .get_feature_info()
            .map_or(false, |finfo| finfo.has_tsc());

        if has_tsc {
            unsafe {
                assert!(rdtsc() > 0, "rdtsc returned 0, unlikely!");
            }
        }
    }

    #[test]
    fn check_rdtscp() {
        let cpuid = crate::cpuid::CpuId::new();
        let has_rdtscp = cpuid
            .get_extended_function_info()
            .map_or(false, |einfo| einfo.has_rdtscp());

        if has_rdtscp {
            unsafe {
                assert!(rdtscp() > 0, "rdtscp returned 0, unlikely!");
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Miscellaneous utilities for low level networking

extern crate libc;

use packet::PrimitiveValues;
use packet::ip::IpNextHeaderProtocol;
use datalink::NetworkInterface;
use pnet_macros_support::types::u16be;

use std::fmt;
use std::str::FromStr;
use std::u8;
use std::net::{IpAddr, Ipv4Addr};
use std::mem;
use std::slice;

use internal;
use sockets;

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }
}

impl PrimitiveValues for MacAddr {
    type T = (u8, u8, u8, u8, u8, u8);
    fn to_primitive_values(&self) -> (u8, u8, u8, u8, u8, u8) {
        (self.0, self.1, self.2, self.3, self.4, self.5)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt,
               "{:x}:{:x}:{:x}:{:x}:{:x}:{:x}",
               self.0,
               self.1,
               self.2,
               self.3,
               self.4,
               self.5)
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, fmt)
    }
}

// FIXME Is this the right way to do this? Which occurs is an implementation
//       issue rather than actually defined - is it useful to provide these
//       errors, or would it be better to just give ()?
/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

#[test]
fn mac_addr_from_str() {
    assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr(0, 0, 0, 0, 0, 0)));
    assert_eq!("ff:ff:ff:ff:ff:ff".parse(),
               Ok(MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)));
    assert_eq!("12:34:56:78:90:ab".parse(),
               Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
    assert_eq!("::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("0::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("::::0::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:90:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooManyComponents));
    assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
}

#[cfg(target_os = "linux")]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET {
            let sll: *const libc::sockaddr_ll = mem::transmute(sa);
            let mac = MacAddr((*sll).sll_addr[0],
                              (*sll).sll_addr[1],
                              (*sll).sll_addr[2],
                              (*sll).sll_addr[3],
                              (*sll).sll_addr[4],
                              (*sll).sll_addr[5]);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use bindings::bpf;
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK {
            let sdl: *const bpf::sockaddr_dl = mem::transmute(sa);
            let nlen = (*sdl).sdl_nlen as usize;
            let mac = MacAddr((*sdl).sdl_data[nlen] as u8,
                              (*sdl).sdl_data[nlen + 1] as u8,
                              (*sdl).sdl_data[nlen + 2] as u8,
                              (*sdl).sdl_data[nlen + 3] as u8,
                              (*sdl).sdl_data[nlen + 4] as u8,
                              (*sdl).sdl_data[nlen + 5] as u8);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

/// Get a list of available network interfaces for the current machine.
/// Deprecated. Instead use the implementation available for your backend.
/// The default one is at `pnet::datalink::interfaces`.
#[deprecated(note="Moved to datalink::interfaces()")]
#[inline]
pub fn get_network_interfaces() -> Vec<NetworkInterface> {
    ::datalink::interfaces()
}

/// Convert value to byte array
pub trait Octets {
    /// Output type - bytes array
    type Output;

    /// Return value as bytes (big-endian order)
    fn octets(&self) -> Self::Output;
}

impl Octets for u64 {
    type Output = [u8; 8];

    fn octets(&self) -> Self::Output {
        [(*self >> 56) as u8, (*self >> 48) as u8, (*self >> 40) as u8, (*self >> 32) as u8,
         (*self >> 24) as u8, (*self >> 16) as u8, (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u32 {
    type Output = [u8; 4];

    fn octets(&self) -> Self::Output {
        [(*self >> 24) as u8, (*self >> 16) as u8 , (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u16 {
    type Output = [u8; 2];

    fn octets(&self) -> Self::Output {
        [(*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u8 {
    type Output = [u8; 1];

    fn octets(&self) -> Self::Output {
        [*self]
    }
}

/// Calculates a checksum. Used by ipv4 and icmp. The two bytes starting at `skipword * 2` will be
/// ignored. Supposed to be the checksum field, which is regarded as zero during calculation.
pub fn checksum(data: &[u8], skipword: usize) -> u16be {
    let mut sum = sum_be_words(data, skipword);
    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }
    !sum as u16
}

/// Calculate the checksum for a packet built on IPv4. Used by udp and tcp.
pub fn ipv4_checksum(data: &[u8],
                     ipv4_source: Ipv4Addr,
                     ipv4_destination: Ipv4Addr,
                     next_level_protocol: IpNextHeaderProtocol,
                     skipword: usize)
    -> u16be {
    let mut sum = 0u32;

    // Checksum pseudo-header
    sum += ipv4_word_sum(ipv4_source);
    sum += ipv4_word_sum(ipv4_destination);

    let IpNextHeaderProtocol(next_level_protocol) = next_level_protocol;
    sum += next_level_protocol as u32;

    sum += data.len() as u32;

    // Checksum packet header and data
    sum += sum_be_words(data, skipword);

    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }

    !sum as u16
}

fn ipv4_word_sum(ip: Ipv4Addr) -> u32 {
    let octets = ip.octets();
    ((octets[0] as u32) << 8 | octets[1] as u32) + ((octets[2] as u32) << 8 | octets[3] as u32)
}

/// Sum all words (16 bit chunks) in the given data. The word at word offset
/// `skipword` will be skipped. Each word is treated as big endian.
fn sum_be_words(data: &[u8], skipword: usize) -> u32 {
    let len = data.len();
    let wdata: &[u16] = unsafe { slice::from_raw_parts(data.as_ptr() as *const u16, len / 2) };
    assert!(skipword <= wdata.len());

    let mut sum = 0u32;
    let mut i = 0;
    while i < skipword {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    i += 1;
    while i < wdata.len() {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    // If the length is odd, make sure to checksum the final byte
    if len & 1 != 0 {
        sum += (unsafe { *data.get_unchecked(len - 1) } as u32) << 8;
    }

    sum
}

#[cfg(all(test, feature = "benchmark"))]
mod checksum_benchmarks {
    use super::checksum;
    use test::{Bencher, black_box};

    #[bench]
    fn bench_checksum_small(b: &mut Bencher) {
        let data = vec![99u8; 20];
        b.iter(|| checksum(black_box(&data), 5));
    }

    #[bench]
    fn bench_checksum_large(b: &mut Bencher) {
        let data = vec![123u8; 1024];
        b.iter(|| checksum(black_box(&data), 5));
    }
}
<commit_msg>Add leading zeros in fmt::Display for MacAddr<commit_after>// Copyright (c) 2014, 2015 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Miscellaneous utilities for low level networking

extern crate libc;

use packet::PrimitiveValues;
use packet::ip::IpNextHeaderProtocol;
use datalink::NetworkInterface;
use pnet_macros_support::types::u16be;

use std::fmt;
use std::str::FromStr;
use std::u8;
use std::net::{IpAddr, Ipv4Addr};
use std::mem;
use std::slice;

use internal;
use sockets;

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy, Hash)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }
}

impl PrimitiveValues for MacAddr {
    type T = (u8, u8, u8, u8, u8, u8);
    fn to_primitive_values(&self) -> (u8, u8, u8, u8, u8, u8) {
        (self.0, self.1, self.2, self.3, self.4, self.5)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt,
               "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
               self.0,
               self.1,
               self.2,
               self.3,
               self.4,
               self.5)
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, fmt)
    }
}

// FIXME Is this the right way to do this? Which occurs is an implementation
//       issue rather than actually defined - is it useful to provide these
//       errors, or would it be better to just give ()?
/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

#[test]
fn mac_addr_from_str() {
    assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr(0, 0, 0, 0, 0, 0)));
    assert_eq!("ff:ff:ff:ff:ff:ff".parse(),
               Ok(MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)));
    assert_eq!("12:34:56:78:90:ab".parse(),
               Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
    assert_eq!("::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("0::::::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("::::0::".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooFewComponents));
    assert_eq!("12:34:56:78:90:".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
    assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(),
               Err(ParseMacAddrErr::TooManyComponents));
    assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(),
               Err(ParseMacAddrErr::InvalidComponent));
}

#[cfg(target_os = "linux")]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == libc::AF_PACKET {
            let sll: *const libc::sockaddr_ll = mem::transmute(sa);
            let mac = MacAddr((*sll).sll_addr[0],
                              (*sll).sll_addr[1],
                              (*sll).sll_addr[2],
                              (*sll).sll_addr[3],
                              (*sll).sll_addr[4],
                              (*sll).sll_addr[5]);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

#[cfg(any(target_os = "freebsd", target_os = "macos"))]
fn sockaddr_to_network_addr(sa: *const sockets::SockAddr) -> (Option<MacAddr>, Option<IpAddr>) {
    use bindings::bpf;
    use std::net::SocketAddr;

    unsafe {
        if sa.is_null() {
            (None, None)
        } else if (*sa).sa_family as libc::c_int == bpf::AF_LINK {
            let sdl: *const bpf::sockaddr_dl = mem::transmute(sa);
            let nlen = (*sdl).sdl_nlen as usize;
            let mac = MacAddr((*sdl).sdl_data[nlen] as u8,
                              (*sdl).sdl_data[nlen + 1] as u8,
                              (*sdl).sdl_data[nlen + 2] as u8,
                              (*sdl).sdl_data[nlen + 3] as u8,
                              (*sdl).sdl_data[nlen + 4] as u8,
                              (*sdl).sdl_data[nlen + 5] as u8);

            (Some(mac), None)
        } else {
            let addr = internal::sockaddr_to_addr(mem::transmute(sa),
                                                  mem::size_of::<sockets::SockAddrStorage>());

            match addr {
                Ok(SocketAddr::V4(sa)) => (None, Some(IpAddr::V4(*sa.ip()))),
                Ok(SocketAddr::V6(sa)) => (None, Some(IpAddr::V6(*sa.ip()))),
                Err(_) => (None, None),
            }
        }
    }
}

/// Get a list of available network interfaces for the current machine.
/// Deprecated. Instead use the implementation available for your backend.
/// The default one is at `pnet::datalink::interfaces`.
#[deprecated(note="Moved to datalink::interfaces()")]
#[inline]
pub fn get_network_interfaces() -> Vec<NetworkInterface> {
    ::datalink::interfaces()
}

/// Convert value to byte array
pub trait Octets {
    /// Output type - bytes array
    type Output;

    /// Return value as bytes (big-endian order)
    fn octets(&self) -> Self::Output;
}

impl Octets for u64 {
    type Output = [u8; 8];

    fn octets(&self) -> Self::Output {
        [(*self >> 56) as u8, (*self >> 48) as u8, (*self >> 40) as u8, (*self >> 32) as u8,
         (*self >> 24) as u8, (*self >> 16) as u8, (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u32 {
    type Output = [u8; 4];

    fn octets(&self) -> Self::Output {
        [(*self >> 24) as u8, (*self >> 16) as u8 , (*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u16 {
    type Output = [u8; 2];

    fn octets(&self) -> Self::Output {
        [(*self >> 8) as u8, *self as u8]
    }
}

impl Octets for u8 {
    type Output = [u8; 1];

    fn octets(&self) -> Self::Output {
        [*self]
    }
}

/// Calculates a checksum. Used by ipv4 and icmp. The two bytes starting at `skipword * 2` will be
/// ignored. Supposed to be the checksum field, which is regarded as zero during calculation.
pub fn checksum(data: &[u8], skipword: usize) -> u16be {
    let mut sum = sum_be_words(data, skipword);
    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }
    !sum as u16
}

/// Calculate the checksum for a packet built on IPv4. Used by udp and tcp.
pub fn ipv4_checksum(data: &[u8],
                     ipv4_source: Ipv4Addr,
                     ipv4_destination: Ipv4Addr,
                     next_level_protocol: IpNextHeaderProtocol,
                     skipword: usize)
    -> u16be {
    let mut sum = 0u32;

    // Checksum pseudo-header
    sum += ipv4_word_sum(ipv4_source);
    sum += ipv4_word_sum(ipv4_destination);

    let IpNextHeaderProtocol(next_level_protocol) = next_level_protocol;
    sum += next_level_protocol as u32;

    sum += data.len() as u32;

    // Checksum packet header and data
    sum += sum_be_words(data, skipword);

    while sum >> 16 != 0 {
        sum = (sum >> 16) + (sum & 0xFFFF);
    }

    !sum as u16
}

fn ipv4_word_sum(ip: Ipv4Addr) -> u32 {
    let octets = ip.octets();
    ((octets[0] as u32) << 8 | octets[1] as u32) + ((octets[2] as u32) << 8 | octets[3] as u32)
}

/// Sum all words (16 bit chunks) in the given data. The word at word offset
/// `skipword` will be skipped. Each word is treated as big endian.
fn sum_be_words(data: &[u8], skipword: usize) -> u32 {
    let len = data.len();
    let wdata: &[u16] = unsafe { slice::from_raw_parts(data.as_ptr() as *const u16, len / 2) };
    assert!(skipword <= wdata.len());

    let mut sum = 0u32;
    let mut i = 0;
    while i < skipword {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    i += 1;
    while i < wdata.len() {
        sum += u16::from_be(unsafe { *wdata.get_unchecked(i) }) as u32;
        i += 1;
    }
    // If the length is odd, make sure to checksum the final byte
    if len & 1 != 0 {
        sum += (unsafe { *data.get_unchecked(len - 1) } as u32) << 8;
    }

    sum
}

#[cfg(all(test, feature = "benchmark"))]
mod checksum_benchmarks {
    use super::checksum;
    use test::{Bencher, black_box};

    #[bench]
    fn bench_checksum_small(b: &mut Bencher) {
        let data = vec![99u8; 20];
        b.iter(|| checksum(black_box(&data), 5));
    }

    #[bench]
    fn bench_checksum_large(b: &mut Bencher) {
        let data = vec![123u8; 1024];
        b.iter(|| checksum(black_box(&data), 5));
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::convert::{From, Into};
use std::ops::*;

use imgui_sys::ImVec2;

#[derive(Copy, Clone)]
pub struct Vec2 {
    pub x: f32,
    pub y: f32,
}

impl Vec2 {
    pub fn new(x: f32, y: f32) -> Self {
        Self { x: x, y: y }
    }
}

impl From<ImVec2> for Vec2 {
    fn from(v: ImVec2) -> Self {
        Self { x: v.x, y: v.y }
    }
}

impl Into<ImVec2> for Vec2 {
    fn into(self) -> ImVec2 {
        ImVec2::new(self.x, self.y)
    }
}

impl Add for Vec2 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Self {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

impl Sub for Vec2 {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        Self {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
        }
    }
}

impl Mul<f32> for Vec2 {
    type Output = Self;

    fn mul(self, rhs: f32) -> Self {
        Self {
            x: self.x * rhs,
            y: self.y * rhs,
        }
    }
}

impl Div<f32> for Vec2 {
    type Output = Self;

    fn div(self, rhs: f32) -> Self {
        Self {
            x: self.x / rhs,
            y: self.y / rhs,
        }
    }
}
<commit_msg>Remove redundant struct field names<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::convert::{From, Into};
use std::ops::*;

use imgui_sys::ImVec2;

#[derive(Copy, Clone)]
pub struct Vec2 {
    pub x: f32,
    pub y: f32,
}

impl Vec2 {
    pub fn new(x: f32, y: f32) -> Self {
        Self { x, y }
    }
}

impl From<ImVec2> for Vec2 {
    fn from(v: ImVec2) -> Self {
        Self { x: v.x, y: v.y }
    }
}

impl Into<ImVec2> for Vec2 {
    fn into(self) -> ImVec2 {
        ImVec2::new(self.x, self.y)
    }
}

impl Add for Vec2 {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Self {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

impl Sub for Vec2 {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        Self {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
        }
    }
}

impl Mul<f32> for Vec2 {
    type Output = Self;

    fn mul(self, rhs: f32) -> Self {
        Self {
            x: self.x * rhs,
            y: self.y * rhs,
        }
    }
}

impl Div<f32> for Vec2 {
    type Output = Self;

    fn div(self, rhs: f32) -> Self {
        Self {
            x: self.x / rhs,
            y: self.y / rhs,
        }
    }
}
<|endoftext|>


<commit_before>pub use platform::View;

#[derive(Copy, Clone)]
pub struct DrawableGeometry {
    pub view_size: (u32, u32),
    pub margins: (u32, u32, u32, u32),
    pub position: (i32, i32),
    pub hidpi_factor: f32,
}

/// View events

#[derive(Debug, Clone)]
pub enum ViewEvent {
    GeometryDidChange,
    MouseWheel(MouseScrollDelta, TouchPhase),
    MouseInput(ElementState, MouseButton),
    MouseMoved(i32, i32),
}

#[derive(Debug, Clone)]
pub enum TouchPhase {
    Started,
    Moved,
    Ended,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ElementState {
    Pressed,
    Released,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
    Left,
    Right,
    Middle,
}

#[derive(Debug, Clone)]
pub enum MouseScrollDelta {
	LineDelta(f32, f32),
	PixelDelta(f32, f32)
}
<commit_msg>debug<commit_after>pub use platform::View;

#[derive(Debug, Copy, Clone)]
pub struct DrawableGeometry {
    pub view_size: (u32, u32),
    pub margins: (u32, u32, u32, u32),
    pub position: (i32, i32),
    pub hidpi_factor: f32,
}

/// View events

#[derive(Debug, Clone)]
pub enum ViewEvent {
    GeometryDidChange,
    MouseWheel(MouseScrollDelta, TouchPhase),
    MouseInput(ElementState, MouseButton),
    MouseMoved(i32, i32),
}

#[derive(Debug, Clone)]
pub enum TouchPhase {
    Started,
    Moved,
    Ended,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ElementState {
    Pressed,
    Released,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MouseButton {
    Left,
    Right,
    Middle,
}

#[derive(Debug, Clone)]
pub enum MouseScrollDelta {
	LineDelta(f32, f32),
	PixelDelta(f32, f32)
}
<|endoftext|>


<commit_before>use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;

#[deriving(Copy)]
pub struct SineWave(pub f64);

impl Fn<(f64, ), f64> for SineWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SineWave(frequency) = *self;
        FloatMath::sin(t * frequency * 2.0 * PI)
    }
}

#[deriving(Copy)]
pub struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SineWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

#[deriving(Copy)]
pub struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor() - 0.5
    }
}

#[deriving(Copy)]
pub struct TriangleWave(pub f64);

impl Fn<(f64, ), f64> for TriangleWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let TriangleWave(frequency) = *self;
        let sawtooth_wave = SawtoothWave(frequency);
        (sawtooth_wave(t).abs() - 0.25) * 4.0
    }
}

#[deriving(Copy)]
pub struct TangentWave(pub f64);

impl Fn<(f64, ), f64> for TangentWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let TangentWave(frequency) = *self;
        ((FloatMath::tan(t * frequency * PI) - 0.5) / 4.0).max(-1.0).min(1.0)
    }
}

#[deriving(Copy)]
// http://computermusicresource.com/Simple.bell.tutorial.html
pub struct Bell(pub f64, pub f64, pub f64);

impl Fn<(f64, ), f64> for Bell {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let Bell(frequency, attack, decay) = *self;

        let envelope = |t: f64, f: f64| -> f64 {
            let tf = t / f;
            if tf < attack {
                (tf / attack).min(1.0)
            } else {
                (1.0 - (tf / decay)).max(0.0)
            }
        };

        let h1 = SineWave(frequency * 0.56);
        let h2 = SineWave(frequency * 0.92);
        let h3 = SineWave(frequency * 1.19);
        let h4 = SineWave(frequency * 1.71);
        let h5 = SineWave(frequency * 2.00);
        let h6 = SineWave(frequency * 2.74);
        let h7 = SineWave(frequency * 3.00);
        let h8 = SineWave(frequency * 3.76);
        let h9 = SineWave(frequency * 4.07);

        (h1(t) * 1.0 * envelope(t, 1.0) +
         h2(t) * 0.5 * envelope(t, 2.0) +
         h3(t) * 0.25 * envelope(t, 4.0) +
         h4(t) * 0.125 * envelope(t, 6.0) +
         h5(t) * 0.0625 * envelope(t, 8.4) +
         h6(t) * 0.03125 * envelope(t, 10.8) +
         h7(t) * 0.015625 * envelope(t, 13.6) +
         h8(t) * 0.0078125 * envelope(t, 16.4) +
         h9(t) * 0.00390625 * envelope(t, 19.6)) / 2.0
    }
}

/// Bastardised generic Karplus-Strong synthesis.
/// Try a Sawtooth, or even a Bell wave.
///
/// `attack` in seconds
/// `decay` in seconds
/// `sharpness` 0-1 is decent
#[deriving(Copy)]
pub struct KarplusStrong<'a, F>(pub F, pub f64, pub f64, pub f64, pub f64);

impl<'a, F> Fn<(f64, ), f64> for KarplusStrong<'a, F> where F: Fn<(f64, ), f64> {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let KarplusStrong(ref wave, attack, decay, sharpness, sample_rate) = *self;

        let tick = 1.0 / sample_rate;

        let envelope = |t: f64, f: f64| -> f64 {
            let tf = t / f;
            if tf < attack {
                (tf / attack).min(1.0)
            } else {
                (1.0 - (tf / decay)).max(0.0)
            }
        };

        // Pretend we have a delay feature in synthrs, manually unroll delay loops
        // Any given sample at any given time will have "imaginary past" loops in it
        range(0, 10u).fold(0.0, |acc, i| {
            acc + wave.call((t - tick * i as f64, ))
                * envelope(t + tick * i as f64, decay)
                * sharpness.powf(i as f64)
        }) * envelope(t, decay)
    }
}
<commit_msg>Refactor Bell wave<commit_after>use std::f64::consts::PI;
use std::num::Float;
use std::num::FloatMath;

#[deriving(Copy)]
pub struct SineWave(pub f64);

impl Fn<(f64, ), f64> for SineWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SineWave(frequency) = *self;
        FloatMath::sin(t * frequency * 2.0 * PI)
    }
}

#[deriving(Copy)]
pub struct SquareWave(pub f64);

impl Fn<(f64, ), f64> for SquareWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SquareWave(frequency) = *self;
        let sin_wave = SineWave(frequency);
        if sin_wave(t).is_positive() { 1.0 } else { -1.0 }
    }
}

#[deriving(Copy)]
pub struct SawtoothWave(pub f64);

impl Fn<(f64, ), f64> for SawtoothWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let SawtoothWave(frequency) = *self;
        let t_factor = t * frequency;
        t_factor - t_factor.floor() - 0.5
    }
}

#[deriving(Copy)]
pub struct TriangleWave(pub f64);

impl Fn<(f64, ), f64> for TriangleWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let TriangleWave(frequency) = *self;
        let sawtooth_wave = SawtoothWave(frequency);
        (sawtooth_wave(t).abs() - 0.25) * 4.0
    }
}

#[deriving(Copy)]
pub struct TangentWave(pub f64);

impl Fn<(f64, ), f64> for TangentWave {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let TangentWave(frequency) = *self;
        ((FloatMath::tan(t * frequency * PI) - 0.5) / 4.0).max(-1.0).min(1.0)
    }
}

#[deriving(Copy)]
// http://computermusicresource.com/Simple.bell.tutorial.html
pub struct Bell(pub f64, pub f64, pub f64);

impl Fn<(f64, ), f64> for Bell {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let Bell(frequency, attack, decay) = *self;

        let envelope = |t: f64, f: f64| -> f64 {
            let tf = t / f;
            if tf < attack {
                (tf / attack).min(1.0)
            } else {
                (1.0 - (tf / decay)).max(0.0)
            }
        };

        // Frequency, amplitude, decay
        let harmonics_table: [(f64, f64, f64); 9] = [
            (0.56, 1.5,        1.0),
            (0.92, 0.5,        2.0),
            (1.19, 0.25,       4.0),
            (1.71, 0.125,      6.0),
            (2.00, 0.0625,     8.4),
            (2.74, 0.03125,    10.8),
            (3.00, 0.015625,   13.6),
            (3.76, 0.0078125,  16.4),
            (4.07, 0.00390625, 19.6)
        ];

        harmonics_table.iter().fold(0.0, |acc, h| {
            acc + SineWave(frequency * h.0)(t) * h.1 * envelope(t, h.2)
        }) / 2.0
    }
}

/// Bastardised generic Karplus-Strong synthesis.
/// Try a Sawtooth, or even a Bell wave.
///
/// `attack` in seconds
/// `decay` in seconds
/// `sharpness` 0-1 is decent
#[deriving(Copy)]
pub struct KarplusStrong<'a, F>(pub F, pub f64, pub f64, pub f64, pub f64);

impl<'a, F> Fn<(f64, ), f64> for KarplusStrong<'a, F> where F: Fn<(f64, ), f64> {
    extern "rust-call" fn call(&self, (t, ): (f64, )) -> f64 {
        let KarplusStrong(ref wave, attack, decay, sharpness, sample_rate) = *self;

        let tick = 1.0 / sample_rate;

        let envelope = |t: f64, f: f64| -> f64 {
            let tf = t / f;
            if tf < attack {
                (tf / attack).min(1.0)
            } else {
                (1.0 - (tf / decay)).max(0.0)
            }
        };

        // Pretend we have a delay feature in synthrs, manually unroll delay loops
        // Any given sample at any given time will have "imaginary past" loops in it
        range(0, 10u).fold(0.0, |acc, i| {
            acc + wave.call((t - tick * i as f64, ))
                * envelope(t + tick * i as f64, decay)
                * sharpness.powf(i as f64)
        }) * envelope(t, decay)
    }
}
<|endoftext|>


<commit_before>
// Copyright 2020 John Hanley.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// The software is provided "AS IS", without warranty of any kind, express or
// implied, including but not limited to the warranties of merchantability,
// fitness for a particular purpose and noninfringement. In no event shall
// the authors or copyright holders be liable for any claim, damages or
// other liability, whether in an action of contract, tort or otherwise,
// arising from, out of or in connection with the software or the use or
// other dealings in the software.

use std::env;

const MAX_ITER: i32 = 255;  // Rust lacks adequate support for default args.

fn mandelbrot_set(xc: f64, yc: f64, sz: f64, px_resolution: i32) {
    let step = (2.0 * sz) / (px_resolution as f64 - 1.0);

    for j in 0 .. px_resolution - 1 {
        for i in 0 .. px_resolution - 1 {
            let x0 = xc - sz + step * i as f64;
            let y0 = yc - sz + step * j as f64;
            plot(_cycles_to_escape(x0, y0));
        }
    }
}

fn _cycles_to_escape(x0: f64, y0: f64) -> i32 {
    let mut x = 0.0;
    let mut y = 0.0;
    let mut i = 0;
    while x * x + y * y <= 4.0 && i < MAX_ITER {
        x = x * x - y * y + x0;
        y = 2.0 * x * y + y0;
        i += 1;
    }
    return i;
}

fn plot(grey_value: i32) {
    let v = grey_value;
    println!("{} {} {}", v, v, v);
}

fn getenv(key: &str) -> i32 {
    let s: String;
    match env::var(key) {
        Ok(v) => s = v,
        Err(_) => s = "400".to_string()
    }
    return stoi(s);
}

fn stoi(s: String) -> i32 {
    return s.parse::<i32>().unwrap();
}

fn stof(s: String) -> f64 {
    return s.parse::<f64>().unwrap();
}

fn main() {
    let px_resolution = getenv("MSET_PX_RESOLUTION");
    let args: Vec<String> = env::args().into_iter().collect();
    let xc = stof(args[1].to_string());
    let yc = stof(args[2].to_string());
    let sz = stof(args[3].to_string());

    println!("P3\n{} {}\n255", px_resolution, px_resolution);
    mandelbrot_set(xc, yc, sz, px_resolution);
}
<commit_msg>Rust tuple assign<commit_after>
// Copyright 2020 John Hanley.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// The software is provided "AS IS", without warranty of any kind, express or
// implied, including but not limited to the warranties of merchantability,
// fitness for a particular purpose and noninfringement. In no event shall
// the authors or copyright holders be liable for any claim, damages or
// other liability, whether in an action of contract, tort or otherwise,
// arising from, out of or in connection with the software or the use or
// other dealings in the software.

use std::env;

const MAX_ITER: i32 = 255;  // Rust lacks adequate support for default args.

fn mandelbrot_set(xc: f64, yc: f64, sz: f64, px_resolution: i32) {
    let step = (2.0 * sz) / (px_resolution as f64 - 1.0);

    for j in 0 .. px_resolution {
        for i in 0 .. px_resolution {
            let x0 = xc - sz + step * i as f64;
            let y0 = yc - sz + step * j as f64;
            plot(_cycles_to_escape(x0, y0));
        }
    }
}

fn _cycles_to_escape(x0: f64, y0: f64) -> i32 {
    let mut x = 0.0;
    let mut y = 0.0;
    let mut i = 0;
    while x * x + y * y <= 4.0 && i < MAX_ITER {
        let (a, b) = (x * x - y * y + x0, 2.0 * x * y + y0);
        x = a;
        y = b; // rust lacks tuple unpack, aka destructuring assignment
        i += 1;
        // println!("{} {} {} {} {}", x0, y0, i, x, y);
    }
    return i;
}

fn plot(grey_value: i32) {
    let v = grey_value;
    println!("{} {} {}", v, v, v);
}

fn getenv(key: &str) -> i32 {
    let s: String;
    match env::var(key) {
        Ok(v) => s = v,
        Err(_) => s = "400".to_string()
    }
    return stoi(s);
}

fn stoi(s: String) -> i32 {
    return s.parse::<i32>().unwrap();
}

fn stof(s: String) -> f64 {
    return s.parse::<f64>().unwrap();
}

fn main() {
    let px_resolution = getenv("MSET_PX_RESOLUTION");
    let args: Vec<String> = env::args().into_iter().collect();
    let xc = stof(args[1].to_string());
    let yc = stof(args[2].to_string());
    let sz = stof(args[3].to_string());

    println!("P3\n{} {}\n255", px_resolution, px_resolution);
    mandelbrot_set(xc, yc, sz, px_resolution);
}
<|endoftext|>