<commit_before>#![allow(dead_code)]
/// The XML structure for a cobatura report is roughly as follows:
/// ```xml
/// <coverage lines-valid="5" lines-covered="0" line-rate="0.0" branches-valid="0"
/// branches-covered="0" branch-rate="0.0" version="1.9" timestamp="...">
///   <sources>
///     <source>PATH</source>
///     ...
///   </sources>
///
///   <packages>
///     <package name=".."  line-rate="0.0" branch-rate="0.0" complexity="0.0">
///       <classes>
///         <class name="Main" filename="main.rs" line-rate="0.0" branch-rate="0.0" complexity="0.0">
///           <methods>
///             <method name="main" signature="()" line-rate="0.0" branch-rate="0.0">
///               <lines>
///                 <line number="1" hits="5" branch="false"/>
///                 <line number="3" hits="2" branch="true">
///                   <conditions>
///                     <condition number="0" type="jump" coverage="50%"/>
///                     ...
///                   </conditions>
///                 </line>
///               </lines>
///             </method>
///             ...
///           </methods>
///
///           <lines>
///             <line number="10" hits="4" branch="false"/>
///           </lines>
///         </class>
///         ...
///       </classes>
///     </package>
///     ...
///   </packages>
/// </coverage>
/// ```
use std::collections::HashSet;
use std::error;
use std::fmt;
use std::fs::File;
use std::io::{Cursor, Write};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

use quick_xml::{
    events::{BytesDecl, BytesEnd, BytesStart, Event},
    Writer,
};

use chrono::offset::Utc;

use crate::config::Config;
use crate::traces::{CoverageStat, Trace, TraceMap};

pub fn report(traces: &TraceMap, config: &Config) -> Result<(), Error> {
    let result = Report::render(config, traces)?;
    result.export(config)
}

#[derive(Debug)]
pub enum Error {
    Unknown,
    ExportError(quick_xml::Error),
}

impl error::Error for Error {
    #[inline]
    fn description(&self) -> &str {
        match self {
            Error::ExportError(_) => "Error exporting xml file",
            Error::Unknown => "Unknown Error",
        }
    }

    #[inline]
    fn cause(&self) -> Option<&dyn error::Error> {
        None
    }
}

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::ExportError(ref e) => write!(f, "Export Error {}", e),
            Error::Unknown => write!(f, "Unknown Error"),
        }
    }
}

#[derive(Debug)]
pub struct Report {
    timestamp: i64,
    lines_covered: usize,
    lines_valid: usize,
    line_rate: f64,
    branches_covered: usize,
    branches_valid: usize,
    branch_rate: f64,
    sources: Vec<PathBuf>,
    packages: Vec<Package>,
}

impl Report {
    pub fn render(config: &Config, traces: &TraceMap) -> Result<Self, Error> {
        let timestamp = Utc::now().timestamp();
        let sources = render_sources(config);
        let packages = render_packages(config, traces);
        let mut line_rate = 0.0;
        let mut branch_rate = 0.0;

        if packages.len() > 0 {
            line_rate = packages.iter().map(|x| x.line_rate).sum::<f64>() / packages.len() as f64;
            branch_rate =
                packages.iter().map(|x| x.branch_rate).sum::<f64>() / packages.len() as f64;
        }

        Ok(Report {
            timestamp: timestamp,
            lines_covered: traces.total_covered(),
            lines_valid: traces.total_coverable(),
            line_rate: line_rate,
            branches_covered: 0,
            branches_valid: 0,
            branch_rate: branch_rate,
            sources: sources,
            packages: packages,
        })
    }

    pub fn export(&self, config: &Config) -> Result<(), Error> {
        let file_path = config.output_directory.join("cobertura.xml");
        let mut file =
            File::create(file_path).map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))?;

        let mut writer = Writer::new(Cursor::new(vec![]));
        writer
            .write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))
            .map_err(|e| Error::ExportError(e))?;

        let cov_tag = b"coverage";
        let mut cov = BytesStart::borrowed(cov_tag, cov_tag.len());
        cov.push_attribute(("lines-covered", self.lines_covered.to_string().as_ref()));
        cov.push_attribute(("lines-valid", self.lines_valid.to_string().as_ref()));
        cov.push_attribute(("line-rate", self.line_rate.to_string().as_ref()));
        cov.push_attribute((
            "branches-covered",
            self.branches_covered.to_string().as_ref(),
        ));
        cov.push_attribute(("branches-valid", self.branches_valid.to_string().as_ref()));
        cov.push_attribute(("branch-rate", self.branch_rate.to_string().as_ref()));
        cov.push_attribute(("complexity", "0"));
        cov.push_attribute(("version", "1.9"));

        let secs = match SystemTime::now().duration_since(UNIX_EPOCH) {
            Ok(s) => s.as_secs().to_string(),
            Err(_) => String::from("0"),
        };
        cov.push_attribute(("timestamp", secs.as_ref()));

        writer
            .write_event(Event::Start(cov))
            .map_err(|e| Error::ExportError(e))?;

        self.export_header(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        self.export_packages(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        writer
            .write_event(Event::End(BytesEnd::borrowed(cov_tag)))
            .map_err(|e| Error::ExportError(e))?;

        let result = writer.into_inner().into_inner();
        file.write_all(&result)
            .map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))
    }

    fn export_header<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let sources_tag = b"sources";
        let source_tag = b"source";
        writer.write_event(Event::Start(BytesStart::borrowed(
            sources_tag,
            sources_tag.len(),
        )))?;
        for source in self.sources.iter() {
            if let Some(ref path) = source.to_str() {
                writer.write_event(Event::Start(BytesStart::borrowed(
                    source_tag,
                    source_tag.len(),
                )))?;
                writer.write(path.as_bytes())?;
                writer.write_event(Event::End(BytesEnd::borrowed(source_tag)))?;
            }
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(sources_tag)))
            .map(|_| ())
    }

    fn export_packages<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let packages_tag = b"packages";
        let pack_tag = b"package";

        writer.write_event(Event::Start(BytesStart::borrowed(
            packages_tag,
            packages_tag.len(),
        )))?;
        // Export the package
        for package in &self.packages {
            let mut pack = BytesStart::borrowed(pack_tag, pack_tag.len());
            pack.push_attribute(("name", package.name.as_str()));
            pack.push_attribute(("line-rate", package.line_rate.to_string().as_ref()));
            pack.push_attribute(("branch-rate", package.branch_rate.to_string().as_ref()));
            pack.push_attribute(("complexity", package.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(pack))?;
            self.export_classes(&package.classes, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(pack_tag)))?;
        }

        writer
            .write_event(Event::End(BytesEnd::borrowed(packages_tag)))
            .map(|_| ())
    }

    fn export_classes<T: Write>(
        &self,
        classes: &[Class],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let classes_tag = b"classes";
        let class_tag = b"class";
        let methods_tag = b"methods";

        writer.write_event(Event::Start(BytesStart::borrowed(
            classes_tag,
            classes_tag.len(),
        )))?;
        for class in classes {
            let mut c = BytesStart::borrowed(class_tag, class_tag.len());
            c.push_attribute(("name", class.name.as_ref()));
            c.push_attribute(("filename", class.file_name.as_ref()));
            c.push_attribute(("line-rate", class.line_rate.to_string().as_ref()));
            c.push_attribute(("branch-rate", class.branch_rate.to_string().as_ref()));
            c.push_attribute(("complexity", class.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(c))?;
            writer.write_event(Event::Empty(BytesStart::borrowed(
                methods_tag,
                methods_tag.len(),
            )))?;
            self.export_lines(&class.lines, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(class_tag)))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(classes_tag)))
            .map(|_| ())
    }

    fn export_lines<T: Write>(
        &self,
        lines: &[Line],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let lines_tag = b"lines";
        let line_tag = b"line";

        writer.write_event(Event::Start(BytesStart::borrowed(
            lines_tag,
            lines_tag.len(),
        )))?;
        for line in lines {
            let mut l = BytesStart::borrowed(line_tag, line_tag.len());
            match line {
                Line::Plain {
                    ref number,
                    ref hits,
                } => {
                    l.push_attribute(("number", number.to_string().as_ref()));
                    l.push_attribute(("hits", hits.to_string().as_ref()));
                }
                Line::Branch { .. } => {}
            }
            writer.write_event(Event::Empty(l))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(lines_tag)))
            .map(|_| ())
    }
}

fn render_sources(config: &Config) -> Vec<PathBuf> {
    vec![config.get_base_dir()]
}

#[derive(Debug)]
struct Package {
    name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    classes: Vec<Class>,
}

fn render_packages(config: &Config, traces: &TraceMap) -> Vec<Package> {
    let dirs: HashSet<&Path> = traces
        .files()
        .into_iter()
        .filter_map(|x| x.parent())
        .collect();

    dirs.into_iter()
        .map(|x| render_package(config, traces, x))
        .collect()
}

fn render_package(config: &Config, traces: &TraceMap, pkg: &Path) -> Package {
    let name = config.strip_base_dir(pkg).to_str().unwrap().to_string();

    let line_cover = traces.covered_in_path(pkg) as f64;
    let line_rate = line_cover / (traces.coverable_in_path(pkg) as f64);

    Package {
        name: name,
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        classes: render_classes(config, traces, pkg),
    }
}

#[derive(Debug)]
struct Class {
    name: String,
    file_name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    lines: Vec<Line>,
    methods: Vec<Method>,
}

fn render_classes(config: &Config, traces: &TraceMap, pkg: &Path) -> Vec<Class> {
    traces
        .files()
        .iter()
        .filter(|x| x.parent() == Some(pkg))
        .map(|x| render_class(config, traces, x))
        .collect()
}

// TODO: Cobertura distinguishes between lines outside methods, and methods
// (which also contain lines). As there is currently no way to get traces from
// a particular function only, all traces are put into lines, and the vector
// of methods is empty.
//
// Until this is fixed, the render_method function will panic if called, as it
// cannot be properly implemented.
//
fn render_class(config: &Config, traces: &TraceMap, file: &Path) -> Class {
    let name = file
        .file_stem()
        .map(|x| x.to_str().unwrap())
        .unwrap_or_default()
        .to_string();

    let file_name = config.strip_base_dir(file).to_str().unwrap().to_string();

    let covered = traces.covered_in_path(file) as f64;
    let line_rate = covered / traces.coverable_in_path(file) as f64;
    let lines = traces
        .get_child_traces(file)
        .iter()
        .map(|x| render_line(x))
        .collect();

    Class {
        name: name,
        file_name: file_name,
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        lines: lines,
        methods: vec![],
    }
}

#[derive(Debug)]
struct Method {
    name: String,
    signature: String,
    line_rate: f64,
    branch_rate: f64,
    lines: Vec<Line>,
}

fn render_methods() -> Vec<Method> {
    unimplemented!()
}

fn render_method() -> Method {
    unimplemented!()
}

#[derive(Debug)]
enum Line {
    Plain {
        number: usize,
        hits: usize,
    },

    Branch {
        number: usize,
        hits: usize,
        conditions: Vec<Condition>,
    },
}

fn render_line(trace: &Trace) -> Line {
    match &trace.stats {
        CoverageStat::Line(hits) => Line::Plain {
            number: trace.line as usize,
            hits: *hits as usize,
        },

        // TODO: Branches in cobertura are given a fresh number as a label,
        // which would require having some form of context when rendering.
        //
        _ => panic!("Not currently supported"),
    }
}

#[derive(Debug)]
struct Condition {
    number: usize,
    cond_type: ConditionType,
    coverage: f64,
}

// Condition types
#[derive(Debug)]
enum ConditionType {
    Jump,
}
<commit_msg>remove deprecated Error::description<commit_after>#![allow(dead_code)]
/// The XML structure for a cobatura report is roughly as follows:
/// ```xml
/// <coverage lines-valid="5" lines-covered="0" line-rate="0.0" branches-valid="0"
/// branches-covered="0" branch-rate="0.0" version="1.9" timestamp="...">
///   <sources>
///     <source>PATH</source>
///     ...
///   </sources>
///
///   <packages>
///     <package name=".."  line-rate="0.0" branch-rate="0.0" complexity="0.0">
///       <classes>
///         <class name="Main" filename="main.rs" line-rate="0.0" branch-rate="0.0" complexity="0.0">
///           <methods>
///             <method name="main" signature="()" line-rate="0.0" branch-rate="0.0">
///               <lines>
///                 <line number="1" hits="5" branch="false"/>
///                 <line number="3" hits="2" branch="true">
///                   <conditions>
///                     <condition number="0" type="jump" coverage="50%"/>
///                     ...
///                   </conditions>
///                 </line>
///               </lines>
///             </method>
///             ...
///           </methods>
///
///           <lines>
///             <line number="10" hits="4" branch="false"/>
///           </lines>
///         </class>
///         ...
///       </classes>
///     </package>
///     ...
///   </packages>
/// </coverage>
/// ```
use std::collections::HashSet;
use std::error;
use std::fmt;
use std::fs::File;
use std::io::{Cursor, Write};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

use quick_xml::{
    events::{BytesDecl, BytesEnd, BytesStart, Event},
    Writer,
};

use chrono::offset::Utc;

use crate::config::Config;
use crate::traces::{CoverageStat, Trace, TraceMap};

pub fn report(traces: &TraceMap, config: &Config) -> Result<(), Error> {
    let result = Report::render(config, traces)?;
    result.export(config)
}

#[derive(Debug)]
pub enum Error {
    Unknown,
    ExportError(quick_xml::Error),
}

impl error::Error for Error {
    #[inline]
    fn cause(&self) -> Option<&dyn error::Error> {
        None
    }
}

impl fmt::Display for Error {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::ExportError(ref e) => write!(f, "Export Error {}", e),
            Error::Unknown => write!(f, "Unknown Error"),
        }
    }
}

#[derive(Debug)]
pub struct Report {
    timestamp: i64,
    lines_covered: usize,
    lines_valid: usize,
    line_rate: f64,
    branches_covered: usize,
    branches_valid: usize,
    branch_rate: f64,
    sources: Vec<PathBuf>,
    packages: Vec<Package>,
}

impl Report {
    pub fn render(config: &Config, traces: &TraceMap) -> Result<Self, Error> {
        let timestamp = Utc::now().timestamp();
        let sources = render_sources(config);
        let packages = render_packages(config, traces);
        let mut line_rate = 0.0;
        let mut branch_rate = 0.0;

        if packages.len() > 0 {
            line_rate = packages.iter().map(|x| x.line_rate).sum::<f64>() / packages.len() as f64;
            branch_rate =
                packages.iter().map(|x| x.branch_rate).sum::<f64>() / packages.len() as f64;
        }

        Ok(Report {
            timestamp: timestamp,
            lines_covered: traces.total_covered(),
            lines_valid: traces.total_coverable(),
            line_rate: line_rate,
            branches_covered: 0,
            branches_valid: 0,
            branch_rate: branch_rate,
            sources: sources,
            packages: packages,
        })
    }

    pub fn export(&self, config: &Config) -> Result<(), Error> {
        let file_path = config.output_directory.join("cobertura.xml");
        let mut file =
            File::create(file_path).map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))?;

        let mut writer = Writer::new(Cursor::new(vec![]));
        writer
            .write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))
            .map_err(|e| Error::ExportError(e))?;

        let cov_tag = b"coverage";
        let mut cov = BytesStart::borrowed(cov_tag, cov_tag.len());
        cov.push_attribute(("lines-covered", self.lines_covered.to_string().as_ref()));
        cov.push_attribute(("lines-valid", self.lines_valid.to_string().as_ref()));
        cov.push_attribute(("line-rate", self.line_rate.to_string().as_ref()));
        cov.push_attribute((
            "branches-covered",
            self.branches_covered.to_string().as_ref(),
        ));
        cov.push_attribute(("branches-valid", self.branches_valid.to_string().as_ref()));
        cov.push_attribute(("branch-rate", self.branch_rate.to_string().as_ref()));
        cov.push_attribute(("complexity", "0"));
        cov.push_attribute(("version", "1.9"));

        let secs = match SystemTime::now().duration_since(UNIX_EPOCH) {
            Ok(s) => s.as_secs().to_string(),
            Err(_) => String::from("0"),
        };
        cov.push_attribute(("timestamp", secs.as_ref()));

        writer
            .write_event(Event::Start(cov))
            .map_err(|e| Error::ExportError(e))?;

        self.export_header(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        self.export_packages(&mut writer)
            .map_err(|e| Error::ExportError(e))?;

        writer
            .write_event(Event::End(BytesEnd::borrowed(cov_tag)))
            .map_err(|e| Error::ExportError(e))?;

        let result = writer.into_inner().into_inner();
        file.write_all(&result)
            .map_err(|e| Error::ExportError(quick_xml::Error::Io(e)))
    }

    fn export_header<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let sources_tag = b"sources";
        let source_tag = b"source";
        writer.write_event(Event::Start(BytesStart::borrowed(
            sources_tag,
            sources_tag.len(),
        )))?;
        for source in self.sources.iter() {
            if let Some(ref path) = source.to_str() {
                writer.write_event(Event::Start(BytesStart::borrowed(
                    source_tag,
                    source_tag.len(),
                )))?;
                writer.write(path.as_bytes())?;
                writer.write_event(Event::End(BytesEnd::borrowed(source_tag)))?;
            }
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(sources_tag)))
            .map(|_| ())
    }

    fn export_packages<T: Write>(&self, writer: &mut Writer<T>) -> Result<(), quick_xml::Error> {
        let packages_tag = b"packages";
        let pack_tag = b"package";

        writer.write_event(Event::Start(BytesStart::borrowed(
            packages_tag,
            packages_tag.len(),
        )))?;
        // Export the package
        for package in &self.packages {
            let mut pack = BytesStart::borrowed(pack_tag, pack_tag.len());
            pack.push_attribute(("name", package.name.as_str()));
            pack.push_attribute(("line-rate", package.line_rate.to_string().as_ref()));
            pack.push_attribute(("branch-rate", package.branch_rate.to_string().as_ref()));
            pack.push_attribute(("complexity", package.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(pack))?;
            self.export_classes(&package.classes, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(pack_tag)))?;
        }

        writer
            .write_event(Event::End(BytesEnd::borrowed(packages_tag)))
            .map(|_| ())
    }

    fn export_classes<T: Write>(
        &self,
        classes: &[Class],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let classes_tag = b"classes";
        let class_tag = b"class";
        let methods_tag = b"methods";

        writer.write_event(Event::Start(BytesStart::borrowed(
            classes_tag,
            classes_tag.len(),
        )))?;
        for class in classes {
            let mut c = BytesStart::borrowed(class_tag, class_tag.len());
            c.push_attribute(("name", class.name.as_ref()));
            c.push_attribute(("filename", class.file_name.as_ref()));
            c.push_attribute(("line-rate", class.line_rate.to_string().as_ref()));
            c.push_attribute(("branch-rate", class.branch_rate.to_string().as_ref()));
            c.push_attribute(("complexity", class.complexity.to_string().as_ref()));

            writer.write_event(Event::Start(c))?;
            writer.write_event(Event::Empty(BytesStart::borrowed(
                methods_tag,
                methods_tag.len(),
            )))?;
            self.export_lines(&class.lines, writer)?;
            writer.write_event(Event::End(BytesEnd::borrowed(class_tag)))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(classes_tag)))
            .map(|_| ())
    }

    fn export_lines<T: Write>(
        &self,
        lines: &[Line],
        writer: &mut Writer<T>,
    ) -> Result<(), quick_xml::Error> {
        let lines_tag = b"lines";
        let line_tag = b"line";

        writer.write_event(Event::Start(BytesStart::borrowed(
            lines_tag,
            lines_tag.len(),
        )))?;
        for line in lines {
            let mut l = BytesStart::borrowed(line_tag, line_tag.len());
            match line {
                Line::Plain {
                    ref number,
                    ref hits,
                } => {
                    l.push_attribute(("number", number.to_string().as_ref()));
                    l.push_attribute(("hits", hits.to_string().as_ref()));
                }
                Line::Branch { .. } => {}
            }
            writer.write_event(Event::Empty(l))?;
        }
        writer
            .write_event(Event::End(BytesEnd::borrowed(lines_tag)))
            .map(|_| ())
    }
}

fn render_sources(config: &Config) -> Vec<PathBuf> {
    vec![config.get_base_dir()]
}

#[derive(Debug)]
struct Package {
    name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    classes: Vec<Class>,
}

fn render_packages(config: &Config, traces: &TraceMap) -> Vec<Package> {
    let dirs: HashSet<&Path> = traces
        .files()
        .into_iter()
        .filter_map(|x| x.parent())
        .collect();

    dirs.into_iter()
        .map(|x| render_package(config, traces, x))
        .collect()
}

fn render_package(config: &Config, traces: &TraceMap, pkg: &Path) -> Package {
    let name = config.strip_base_dir(pkg).to_str().unwrap().to_string();

    let line_cover = traces.covered_in_path(pkg) as f64;
    let line_rate = line_cover / (traces.coverable_in_path(pkg) as f64);

    Package {
        name: name,
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        classes: render_classes(config, traces, pkg),
    }
}

#[derive(Debug)]
struct Class {
    name: String,
    file_name: String,
    line_rate: f64,
    branch_rate: f64,
    complexity: f64,
    lines: Vec<Line>,
    methods: Vec<Method>,
}

fn render_classes(config: &Config, traces: &TraceMap, pkg: &Path) -> Vec<Class> {
    traces
        .files()
        .iter()
        .filter(|x| x.parent() == Some(pkg))
        .map(|x| render_class(config, traces, x))
        .collect()
}

// TODO: Cobertura distinguishes between lines outside methods, and methods
// (which also contain lines). As there is currently no way to get traces from
// a particular function only, all traces are put into lines, and the vector
// of methods is empty.
//
// Until this is fixed, the render_method function will panic if called, as it
// cannot be properly implemented.
//
fn render_class(config: &Config, traces: &TraceMap, file: &Path) -> Class {
    let name = file
        .file_stem()
        .map(|x| x.to_str().unwrap())
        .unwrap_or_default()
        .to_string();

    let file_name = config.strip_base_dir(file).to_str().unwrap().to_string();

    let covered = traces.covered_in_path(file) as f64;
    let line_rate = covered / traces.coverable_in_path(file) as f64;
    let lines = traces
        .get_child_traces(file)
        .iter()
        .map(|x| render_line(x))
        .collect();

    Class {
        name: name,
        file_name: file_name,
        line_rate: line_rate,
        branch_rate: 0.0,
        complexity: 0.0,
        lines: lines,
        methods: vec![],
    }
}

#[derive(Debug)]
struct Method {
    name: String,
    signature: String,
    line_rate: f64,
    branch_rate: f64,
    lines: Vec<Line>,
}

fn render_methods() -> Vec<Method> {
    unimplemented!()
}

fn render_method() -> Method {
    unimplemented!()
}

#[derive(Debug)]
enum Line {
    Plain {
        number: usize,
        hits: usize,
    },

    Branch {
        number: usize,
        hits: usize,
        conditions: Vec<Condition>,
    },
}

fn render_line(trace: &Trace) -> Line {
    match &trace.stats {
        CoverageStat::Line(hits) => Line::Plain {
            number: trace.line as usize,
            hits: *hits as usize,
        },

        // TODO: Branches in cobertura are given a fresh number as a label,
        // which would require having some form of context when rendering.
        //
        _ => panic!("Not currently supported"),
    }
}

#[derive(Debug)]
struct Condition {
    number: usize,
    cond_type: ConditionType,
    coverage: f64,
}

// Condition types
#[derive(Debug)]
enum ConditionType {
    Jump,
}
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_trait_def_id, if_sequence, is_else_clause, paths, SpanlessEq};
use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks comparison chains written with `if` that can be
    /// rewritten with `match` and `cmp`.
    ///
    /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
    /// repetitive
    ///
    /// **Known problems:** The match statement may be slower due to the compiler
    /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
    ///
    /// **Example:**
    /// ```rust,ignore
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///     if x > y {
    ///         a()
    ///     } else if x < y {
    ///         b()
    ///     } else {
    ///         c()
    ///     }
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```rust,ignore
    /// use std::cmp::Ordering;
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///      match x.cmp(&y) {
    ///          Ordering::Greater => a(),
    ///          Ordering::Less => b(),
    ///          Ordering::Equal => c()
    ///      }
    /// }
    /// ```
    pub COMPARISON_CHAIN,
    style,
    "`if`s that can be rewritten with `match` and `cmp`"
}

declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);

impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if expr.span.from_expansion() {
            return;
        }

        // We only care about the top-most `if` in the chain
        if is_else_clause(cx.tcx, expr) {
            return;
        }

        if parent_node_is_if_const_fn(cx, expr) {
            return;
        }

        // Check that there exists at least one explicit else condition
        let (conds, _) = if_sequence(expr);
        if conds.len() < 2 {
            return;
        }

        for cond in conds.windows(2) {
            if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) =
                (&cond[0].kind, &cond[1].kind)
            {
                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
                    return;
                }

                // Check that both sets of operands are equal
                let mut spanless_eq = SpanlessEq::new(cx);
                let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2);
                let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2);

                if !same_fixed_operands && !same_transposed_operands {
                    return;
                }

                // Check that if the operation is the same, either it's not `==` or the operands are transposed
                if kind1.node == kind2.node {
                    if kind1.node == BinOpKind::Eq {
                        return;
                    }
                    if !same_transposed_operands {
                        return;
                    }
                }

                // Check that the type being compared implements `core::cmp::Ord`
                let ty = cx.typeck_results().expr_ty(lhs1);
                let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));

                if !is_ord {
                    return;
                }
            } else {
                // We only care about comparison chains
                return;
            }
        }
        span_lint_and_help(
            cx,
            COMPARISON_CHAIN,
            expr.span,
            "`if` chain can be rewritten with `match`",
            None,
            "consider rewriting the `if` chain to use `cmp` and `match`",
        )
    }
}

fn kind_is_cmp(kind: BinOpKind) -> bool {
    matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
}

fn parent_node_is_if_const_fn(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
    match cx.tcx.hir().find(cx.tcx.hir().get_parent_item(expr.hir_id)) {
        Some(Node::Item(item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, item.def_id.to_def_id()),
        Some(Node::ImplItem(impl_item)) => rustc_mir::const_eval::is_const_fn(cx.tcx, impl_item.def_id.to_def_id()),
        _ => false,
    }
}
<commit_msg>use `in_constant`<commit_after>use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks comparison chains written with `if` that can be
    /// rewritten with `match` and `cmp`.
    ///
    /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
    /// repetitive
    ///
    /// **Known problems:** The match statement may be slower due to the compiler
    /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
    ///
    /// **Example:**
    /// ```rust,ignore
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///     if x > y {
    ///         a()
    ///     } else if x < y {
    ///         b()
    ///     } else {
    ///         c()
    ///     }
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```rust,ignore
    /// use std::cmp::Ordering;
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///      match x.cmp(&y) {
    ///          Ordering::Greater => a(),
    ///          Ordering::Less => b(),
    ///          Ordering::Equal => c()
    ///      }
    /// }
    /// ```
    pub COMPARISON_CHAIN,
    style,
    "`if`s that can be rewritten with `match` and `cmp`"
}

declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);

impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if expr.span.from_expansion() {
            return;
        }

        // We only care about the top-most `if` in the chain
        if is_else_clause(cx.tcx, expr) {
            return;
        }

        if in_constant(cx, expr.hir_id) {
            return;
        }

        // Check that there exists at least one explicit else condition
        let (conds, _) = if_sequence(expr);
        if conds.len() < 2 {
            return;
        }

        for cond in conds.windows(2) {
            if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) =
                (&cond[0].kind, &cond[1].kind)
            {
                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
                    return;
                }

                // Check that both sets of operands are equal
                let mut spanless_eq = SpanlessEq::new(cx);
                let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2);
                let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2);

                if !same_fixed_operands && !same_transposed_operands {
                    return;
                }

                // Check that if the operation is the same, either it's not `==` or the operands are transposed
                if kind1.node == kind2.node {
                    if kind1.node == BinOpKind::Eq {
                        return;
                    }
                    if !same_transposed_operands {
                        return;
                    }
                }

                // Check that the type being compared implements `core::cmp::Ord`
                let ty = cx.typeck_results().expr_ty(lhs1);
                let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));

                if !is_ord {
                    return;
                }
            } else {
                // We only care about comparison chains
                return;
            }
        }
        span_lint_and_help(
            cx,
            COMPARISON_CHAIN,
            expr.span,
            "`if` chain can be rewritten with `match`",
            None,
            "consider rewriting the `if` chain to use `cmp` and `match`",
        )
    }
}

fn kind_is_cmp(kind: BinOpKind) -> bool {
    matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
}
<|endoftext|>


<commit_before>#![crate_type = "bin"]
#![feature(collections)]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::{WindowTrait, ContainerTrait, WidgetTrait};
use glib::{Type, timeout};
use sysinfo::*;
use gtk::signal::Inhibit;
use gtk::signal::{WidgetSignals, TreeViewSignals};
use std::collections::VecMap;
use std::str::FromStr;

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>) {
    let l = v.len();
    let renderer = gtk::CellRendererText::new().unwrap();

    v.push(gtk::TreeViewColumn::new().unwrap());
    let tmp = v.get_mut(l).unwrap();

    tmp.set_title(title);
    tmp.pack_start(&renderer, true);
    tmp.add_attribute(&renderer, "text", l as i32);
}

fn create_and_fill_model(list_store: &mut gtk::ListStore, pid: i64, name: &str, cpu: f32, memory: u64) {
    if name.len() < 1 {
        return;
    }
    let mut top_level = gtk::TreeIter::new();

    list_store.append(&mut top_level);
    list_store.set_string(&top_level, 0, &format!("{}", pid));
    list_store.set_string(&top_level, 1, name);
    list_store.set_string(&top_level, 2, &format!("{}", cpu));
    list_store.set_string(&top_level, 3, &format!("{}", memory));
}

fn update_window(w: &mut (&mut gtk::ListStore, &mut sysinfo::System)) -> i32 {
    let mut model = w.0.get_model().unwrap();
    let mut iter = gtk::TreeIter::new();

    w.1.refresh();
    let mut entries : VecMap<Processus> = (*w.1.get_processus_list()).clone();
    let mut nb = model.iter_n_children(None);
    let mut i = 0;

    while i < nb {
        if model.iter_nth_child(&mut iter, None, i) {
            let pid : i64 = i64::from_str(&model.get_value(&iter, 0).get_string().unwrap()).unwrap();
            let mut to_delete = false;

            match entries.get(&(pid as usize)) {
                Some(p) => {
                    //w.0.set_string(&iter, 1, &p.name);
                    w.0.set_string(&iter, 2, &format!("{}", p.cpu_usage));
                    w.0.set_string(&iter, 3, &format!("{}", p.memory));
                    to_delete = true;
                }
                None => {
                    w.0.remove(&iter);
                }
            }
            if to_delete {
                entries.remove(&(pid as usize));
                nb = model.iter_n_children(None);
                i += 1;
            }
        } else {
            i += 1;
        }
    }
    for (_, pro) in entries {
        create_and_fill_model(&mut w.0, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }
    1
}

fn main() {
    gtk::init();

    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let mut sys = sysinfo::System::new();

    window.set_title("Processus viewer");
    window.set_window_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    let mut left_tree = gtk::TreeView::new().unwrap();
    let mut scroll = gtk::ScrolledWindow::new(None, None).unwrap();
    let mut current_pid : Option<String> = None;

    scroll.set_min_content_height(800);
    scroll.set_min_content_width(600);

    let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

    append_column("pid", &mut columns);
    append_column("process name", &mut columns);
    append_column("cpu usage", &mut columns);
    append_column("memory usage", &mut columns);

    for i in columns {
        left_tree.append_column(&i);
    }

    let mut list_store = gtk::ListStore::new(&[Type::String, Type::String, Type::String, Type::String]).unwrap();
    sys.refresh();
    for (_, pro) in sys.get_processus_list() {
        create_and_fill_model(&mut list_store, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }

    left_tree.set_model(&list_store.get_model().unwrap());
    left_tree.set_headers_visible(true);
    scroll.add(&left_tree);
    let mut vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    let mut kill_button = gtk::Button::new_with_label("End task").unwrap();

    left_tree.connect_cursor_changed(|tree_view| {
        /*let mut path = gtk::TreePath::new().unwrap();
        tree_view.get_cursor(Some(&mut path), None);
        let mut iter = gtk::TreeIter::new();
        let model = tree_view.get_model().unwrap();

        if model.get_iter(&mut iter, &path) {
            current_pid = Some(model.get_value(&iter, 0).get_string().unwrap());
        }*/
        println!("current pid : {:?}", current_pid);
    });
    kill_button.set_sensitive(false);
    timeout::add(1500, update_window, &mut (&mut list_store, &mut sys));

    vertical_layout.add(&scroll);
    vertical_layout.add(&kill_button);
    window.add(&vertical_layout);
    window.show_all();
    gtk::main();
}
<commit_msg>To avoid compilation failure<commit_after>#![crate_type = "bin"]
#![feature(collections)]

extern crate gtk;
extern crate glib;
extern crate sysinfo;

use gtk::{WindowTrait, ContainerTrait, WidgetTrait};
use glib::{Type, timeout};
use sysinfo::*;
use gtk::signal::Inhibit;
use gtk::signal::{WidgetSignals, TreeViewSignals};
use std::collections::VecMap;
use std::str::FromStr;

fn append_column(title: &str, v: &mut Vec<gtk::TreeViewColumn>) {
    let l = v.len();
    let renderer = gtk::CellRendererText::new().unwrap();

    v.push(gtk::TreeViewColumn::new().unwrap());
    let tmp = v.get_mut(l).unwrap();

    tmp.set_title(title);
    tmp.pack_start(&renderer, true);
    tmp.add_attribute(&renderer, "text", l as i32);
}

fn create_and_fill_model(list_store: &mut gtk::ListStore, pid: i64, name: &str, cpu: f32, memory: u64) {
    if name.len() < 1 {
        return;
    }
    let mut top_level = gtk::TreeIter::new();

    list_store.append(&mut top_level);
    list_store.set_string(&top_level, 0, &format!("{}", pid));
    list_store.set_string(&top_level, 1, name);
    list_store.set_string(&top_level, 2, &format!("{}", cpu));
    list_store.set_string(&top_level, 3, &format!("{}", memory));
}

fn update_window(w: &mut (&mut gtk::ListStore, &mut sysinfo::System)) -> i32 {
    let mut model = w.0.get_model().unwrap();
    let mut iter = gtk::TreeIter::new();

    w.1.refresh();
    let mut entries : VecMap<Processus> = (*w.1.get_processus_list()).clone();
    let mut nb = model.iter_n_children(None);
    let mut i = 0;

    while i < nb {
        if model.iter_nth_child(&mut iter, None, i) {
            let pid : i64 = i64::from_str(&model.get_value(&iter, 0).get_string().unwrap()).unwrap();
            let mut to_delete = false;

            match entries.get(&(pid as usize)) {
                Some(p) => {
                    //w.0.set_string(&iter, 1, &p.name);
                    w.0.set_string(&iter, 2, &format!("{}", p.cpu_usage));
                    w.0.set_string(&iter, 3, &format!("{}", p.memory));
                    to_delete = true;
                }
                None => {
                    w.0.remove(&iter);
                }
            }
            if to_delete {
                entries.remove(&(pid as usize));
                nb = model.iter_n_children(None);
                i += 1;
            }
        } else {
            i += 1;
        }
    }
    for (_, pro) in entries {
        create_and_fill_model(&mut w.0, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }
    1
}

fn main() {
    gtk::init();

    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    let mut sys = sysinfo::System::new();

    window.set_title("Processus viewer");
    window.set_window_position(gtk::WindowPosition::Center);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(true)
    });

    let mut left_tree = gtk::TreeView::new().unwrap();
    let mut scroll = gtk::ScrolledWindow::new(None, None).unwrap();
    let mut current_pid : Option<String> = None;

    scroll.set_min_content_height(800);
    scroll.set_min_content_width(600);

    let mut columns : Vec<gtk::TreeViewColumn> = Vec::new();

    append_column("pid", &mut columns);
    append_column("process name", &mut columns);
    append_column("cpu usage", &mut columns);
    append_column("memory usage", &mut columns);

    for i in columns {
        left_tree.append_column(&i);
    }

    let mut list_store = gtk::ListStore::new(&[Type::String, Type::String, Type::String, Type::String]).unwrap();
    sys.refresh();
    for (_, pro) in sys.get_processus_list() {
        create_and_fill_model(&mut list_store, pro.pid, &pro.name, pro.cpu_usage, pro.memory);
    }

    left_tree.set_model(&list_store.get_model().unwrap());
    left_tree.set_headers_visible(true);
    scroll.add(&left_tree);
    let mut vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    let mut kill_button = gtk::Button::new_with_label("End task").unwrap();

    left_tree.connect_cursor_changed(move |tree_view| {
        /*let mut path = gtk::TreePath::new().unwrap();
        tree_view.get_cursor(Some(&mut path), None);
        let mut iter = gtk::TreeIter::new();
        let model = tree_view.get_model().unwrap();

        if model.get_iter(&mut iter, &path) {
            current_pid = Some(model.get_value(&iter, 0).get_string().unwrap());
        }*/
        println!("current pid : {:?}", current_pid);
    });
    kill_button.set_sensitive(false);
    timeout::add(1500, update_window, &mut (&mut list_store, &mut sys));

    vertical_layout.add(&scroll);
    vertical_layout.add(&kill_button);
    window.add(&vertical_layout);
    window.show_all();
    gtk::main();
}
<|endoftext|>


<commit_before><commit_msg>Apply rustfmt<commit_after><|endoftext|>


<commit_before>use base64;
use line_wrap;
use std::{borrow::Cow, io::Write};
use xml_rs::{
    name::Name,
    namespace::Namespace,
    writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent},
};

use crate::{
    error::{self, Error, ErrorKind, EventKind},
    stream::{Writer, XmlWriteOptions},
    Date, Integer, Uid,
};

static XML_PROLOGUE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
"#;

#[derive(PartialEq)]
enum Element {
    Dictionary,
    Array,
}

pub struct XmlWriter<W: Write> {
    xml_writer: EventWriter<W>,
    stack: Vec<Element>,
    expecting_key: bool,
    written_prologue: bool,
    // Not very nice
    empty_namespace: Namespace,
}

impl<W: Write> XmlWriter<W> {
    pub fn new(writer: W) -> XmlWriter<W> {
        let opts = XmlWriteOptions::default();
        XmlWriter::new_with_options(writer, &opts)
    }

    pub fn new_with_options(writer: W, opts: &XmlWriteOptions) -> XmlWriter<W> {
        let config = EmitterConfig::new()
            .line_separator("\n")
            .indent_string(opts.indent_str.clone())
            .perform_indent(true)
            .write_document_declaration(false)
            .normalize_empty_elements(true)
            .cdata_to_characters(true)
            .keep_element_names_stack(false)
            .autopad_comments(true)
            .pad_self_closing(false);

        XmlWriter {
            xml_writer: EventWriter::new_with_config(writer, config),
            stack: Vec::new(),
            expecting_key: false,
            written_prologue: false,
            empty_namespace: Namespace::empty(),
        }
    }

    fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<(), Error> {
        self.start_element(name)?;
        self.write_value(value)?;
        self.end_element(name)?;
        Ok(())
    }

    fn start_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer
            .write(XmlEvent::StartElement {
                name: Name::local(name),
                attributes: Cow::Borrowed(&[]),
                namespace: Cow::Borrowed(&self.empty_namespace),
            })
            .map_err(from_xml_error)?;
        Ok(())
    }

    fn end_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer
            .write(XmlEvent::EndElement {
                name: Some(Name::local(name)),
            })
            .map_err(from_xml_error)?;
        Ok(())
    }

    fn write_value(&mut self, value: &str) -> Result<(), Error> {
        self.xml_writer
            .write(XmlEvent::Characters(value))
            .map_err(from_xml_error)?;
        Ok(())
    }

    pub fn into_inner(self) -> W {
        self.xml_writer.into_inner()
    }

    fn write_event<F: FnOnce(&mut Self) -> Result<(), Error>>(
        &mut self,
        f: F,
    ) -> Result<(), Error> {
        if !self.written_prologue {
            self.xml_writer
                .inner_mut()
                .write_all(XML_PROLOGUE.as_bytes())
                .map_err(error::from_io_without_position)?;

            self.written_prologue = true;
        }

        f(self)?;

        // If there are no more open tags then write the </plist> element
        if self.stack.is_empty() {
            // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it
            // about the </plist> tag as well.
            self.xml_writer
                .inner_mut()
                .write_all(b"\n</plist>")
                .map_err(error::from_io_without_position)?;
            self.xml_writer
                .inner_mut()
                .flush()
                .map_err(error::from_io_without_position)?;
        }

        Ok(())
    }

    fn write_value_event<F: FnOnce(&mut Self) -> Result<(), Error>>(
        &mut self,
        event_kind: EventKind,
        f: F,
    ) -> Result<(), Error> {
        self.write_event(|this| {
            if this.expecting_key {
                return Err(ErrorKind::UnexpectedEventType {
                    expected: EventKind::DictionaryKeyOrEndCollection,
                    found: event_kind,
                }
                .without_position());
            }
            f(this)?;
            this.expecting_key = this.stack.last() == Some(&Element::Dictionary);
            Ok(())
        })
    }
}

impl<W: Write> Writer for XmlWriter<W> {
    fn write_start_array(&mut self, _len: Option<u64>) -> Result<(), Error> {
        self.write_value_event(EventKind::StartArray, |this| {
            this.start_element("array")?;
            this.stack.push(Element::Array);
            Ok(())
        })
    }

    fn write_start_dictionary(&mut self, _len: Option<u64>) -> Result<(), Error> {
        self.write_value_event(EventKind::StartDictionary, |this| {
            this.start_element("dict")?;
            this.stack.push(Element::Dictionary);
            Ok(())
        })
    }

    fn write_end_collection(&mut self) -> Result<(), Error> {
        self.write_event(|this| {
            match (this.stack.pop(), this.expecting_key) {
                (Some(Element::Dictionary), true) => {
                    this.end_element("dict")?;
                }
                (Some(Element::Array), _) => {
                    this.end_element("array")?;
                }
                (Some(Element::Dictionary), false) | (None, _) => {
                    return Err(ErrorKind::UnexpectedEventType {
                        expected: EventKind::ValueOrStartCollection,
                        found: EventKind::EndCollection,
                    }
                    .without_position());
                }
            }
            this.expecting_key = this.stack.last() == Some(&Element::Dictionary);
            Ok(())
        })
    }

    fn write_boolean(&mut self, value: bool) -> Result<(), Error> {
        self.write_value_event(EventKind::Boolean, |this| {
            let value_str = if value { "true" } else { "false" };
            this.start_element(value_str)?;
            this.end_element(value_str)
        })
    }

    fn write_data(&mut self, value: &[u8]) -> Result<(), Error> {
        self.write_value_event(EventKind::Data, |this| {
            let base64_data = base64_encode_plist(&value, this.stack.len());
            this.write_element_and_value("data", &base64_data)
        })
    }

    fn write_date(&mut self, value: Date) -> Result<(), Error> {
        self.write_value_event(EventKind::Date, |this| {
            this.write_element_and_value("date", &value.to_xml_format())
        })
    }

    fn write_integer(&mut self, value: Integer) -> Result<(), Error> {
        self.write_value_event(EventKind::Integer, |this| {
            this.write_element_and_value("integer", &value.to_string())
        })
    }

    fn write_real(&mut self, value: f64) -> Result<(), Error> {
        self.write_value_event(EventKind::Real, |this| {
            this.write_element_and_value("real", &value.to_string())
        })
    }

    fn write_string(&mut self, value: &str) -> Result<(), Error> {
        self.write_event(|this| {
            if this.expecting_key {
                this.write_element_and_value("key", &*value)?;
                this.expecting_key = false;
            } else {
                this.write_element_and_value("string", &*value)?;
                this.expecting_key = this.stack.last() == Some(&Element::Dictionary);
            }
            Ok(())
        })
    }

    fn write_uid(&mut self, _value: Uid) -> Result<(), Error> {
        Err(ErrorKind::UidNotSupportedInXmlPlist.without_position())
    }
}

pub(crate) fn from_xml_error(err: XmlWriterError) -> Error {
    match err {
        XmlWriterError::Io(err) => ErrorKind::Io(err).without_position(),
        XmlWriterError::DocumentStartAlreadyEmitted
        | XmlWriterError::LastElementNameNotAvailable
        | XmlWriterError::EndElementNameIsNotEqualToLastStartElementName
        | XmlWriterError::EndElementNameIsNotSpecified => unreachable!(),
    }
}

fn base64_encode_plist(data: &[u8], indent: usize) -> String {
    // XML plist data elements are always formatted by apple tools as
    // <data>
    // AAAA..AA (68 characters per line)
    // </data>
    // Allocate space for base 64 string and line endings up front
    const LINE_LEN: usize = 68;
    let mut line_ending = Vec::with_capacity(1 + indent);
    line_ending.push(b'\n');
    (0..indent).for_each(|_| line_ending.push(b'\t'));

    // Find the max length of `data` encoded as a base 64 string with padding
    let base64_max_string_len = data.len() * 4 / 3 + 4;

    // Find the max length of the formatted base 64 string as: max length of the base 64 string
    // + line endings and indents at the start of the string and after every line
    let base64_max_string_len_with_formatting =
        base64_max_string_len + (2 + base64_max_string_len / LINE_LEN) * line_ending.len();

    let mut output = vec![0; base64_max_string_len_with_formatting];

    // Start output with a line ending and indent
    output[..line_ending.len()].copy_from_slice(&line_ending);

    // Encode `data` as a base 64 string
    let base64_string_len =
        base64::encode_config_slice(data, base64::STANDARD, &mut output[line_ending.len()..]);

    // Line wrap the base 64 encoded string
    let line_wrap_len = line_wrap::line_wrap(
        &mut output[line_ending.len()..],
        base64_string_len,
        LINE_LEN,
        &line_wrap::SliceLineEnding::new(&line_ending),
    );

    // Add the final line ending and indent
    output[line_ending.len() + base64_string_len + line_wrap_len..][..line_ending.len()]
        .copy_from_slice(&line_ending);

    // Ensure output is the correct length
    output.truncate(base64_string_len + line_wrap_len + 2 * line_ending.len());
    String::from_utf8(output).expect("base 64 string must be valid utf8")
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use super::*;
    use crate::stream::Event;

    #[test]
    fn streaming_parser() {
        let plist = &[
            Event::StartDictionary(None),
            Event::String("Author".into()),
            Event::String("William Shakespeare".into()),
            Event::String("Lines".into()),
            Event::StartArray(None),
            Event::String("It is a tale told by an idiot,".into()),
            Event::String("Full of sound and fury, signifying nothing.".into()),
            Event::Data((0..128).collect::<Vec<_>>().into()),
            Event::EndCollection,
            Event::String("Death".into()),
            Event::Integer(1564.into()),
            Event::String("Height".into()),
            Event::Real(1.60),
            Event::String("Data".into()),
            Event::Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0].into()),
            Event::String("Birthdate".into()),
            Event::Date(super::Date::from_xml_format("1981-05-16T11:32:06Z").unwrap()),
            Event::String("Comment".into()),
            Event::String("2 < 3".into()), // make sure characters are escaped
            Event::String("BiggestNumber".into()),
            Event::Integer(18446744073709551615u64.into()),
            Event::String("SmallestNumber".into()),
            Event::Integer((-9223372036854775808i64).into()),
            Event::String("IsTrue".into()),
            Event::Boolean(true),
            Event::String("IsNotFalse".into()),
            Event::Boolean(false),
            Event::EndCollection,
        ];

        let mut cursor = Cursor::new(Vec::new());

        {
            let mut plist_w = XmlWriter::new(&mut cursor);

            for item in plist {
                plist_w.write(item).unwrap();
            }
        }

        let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
\t<key>Author</key>
\t<string>William Shakespeare</string>
\t<key>Lines</key>
\t<array>
\t\t<string>It is a tale told by an idiot,</string>
\t\t<string>Full of sound and fury, signifying nothing.</string>
\t\t<data>
\t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy
\t\tMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2Rl
\t\tZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=
\t\t</data>
\t</array>
\t<key>Death</key>
\t<integer>1564</integer>
\t<key>Height</key>
\t<real>1.6</real>
\t<key>Data</key>
\t<data>
\tAAAAvgAAAAMAAAAeAAAA
\t</data>
\t<key>Birthdate</key>
\t<date>1981-05-16T11:32:06Z</date>
\t<key>Comment</key>
\t<string>2 < 3</string>
\t<key>BiggestNumber</key>
\t<integer>18446744073709551615</integer>
\t<key>SmallestNumber</key>
\t<integer>-9223372036854775808</integer>
\t<key>IsTrue</key>
\t<true/>
\t<key>IsNotFalse</key>
\t<false/>
</dict>
</plist>";

        let s = String::from_utf8(cursor.into_inner()).unwrap();

        assert_eq!(s, comparison);
    }
}
<commit_msg>Test custom XML plist indent strings<commit_after>use base64;
use line_wrap;
use std::{borrow::Cow, io::Write};
use xml_rs::{
    name::Name,
    namespace::Namespace,
    writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent},
};

use crate::{
    error::{self, Error, ErrorKind, EventKind},
    stream::{Writer, XmlWriteOptions},
    Date, Integer, Uid,
};

static XML_PROLOGUE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
"#;

#[derive(PartialEq)]
enum Element {
    Dictionary,
    Array,
}

pub struct XmlWriter<W: Write> {
    xml_writer: EventWriter<W>,
    stack: Vec<Element>,
    expecting_key: bool,
    written_prologue: bool,
    // Not very nice
    empty_namespace: Namespace,
}

impl<W: Write> XmlWriter<W> {
    pub fn new(writer: W) -> XmlWriter<W> {
        let opts = XmlWriteOptions::default();
        XmlWriter::new_with_options(writer, &opts)
    }

    pub fn new_with_options(writer: W, opts: &XmlWriteOptions) -> XmlWriter<W> {
        let config = EmitterConfig::new()
            .line_separator("\n")
            .indent_string(opts.indent_str.clone())
            .perform_indent(true)
            .write_document_declaration(false)
            .normalize_empty_elements(true)
            .cdata_to_characters(true)
            .keep_element_names_stack(false)
            .autopad_comments(true)
            .pad_self_closing(false);

        XmlWriter {
            xml_writer: EventWriter::new_with_config(writer, config),
            stack: Vec::new(),
            expecting_key: false,
            written_prologue: false,
            empty_namespace: Namespace::empty(),
        }
    }

    fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<(), Error> {
        self.start_element(name)?;
        self.write_value(value)?;
        self.end_element(name)?;
        Ok(())
    }

    fn start_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer
            .write(XmlEvent::StartElement {
                name: Name::local(name),
                attributes: Cow::Borrowed(&[]),
                namespace: Cow::Borrowed(&self.empty_namespace),
            })
            .map_err(from_xml_error)?;
        Ok(())
    }

    fn end_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer
            .write(XmlEvent::EndElement {
                name: Some(Name::local(name)),
            })
            .map_err(from_xml_error)?;
        Ok(())
    }

    fn write_value(&mut self, value: &str) -> Result<(), Error> {
        self.xml_writer
            .write(XmlEvent::Characters(value))
            .map_err(from_xml_error)?;
        Ok(())
    }

    pub fn into_inner(self) -> W {
        self.xml_writer.into_inner()
    }

    fn write_event<F: FnOnce(&mut Self) -> Result<(), Error>>(
        &mut self,
        f: F,
    ) -> Result<(), Error> {
        if !self.written_prologue {
            self.xml_writer
                .inner_mut()
                .write_all(XML_PROLOGUE.as_bytes())
                .map_err(error::from_io_without_position)?;

            self.written_prologue = true;
        }

        f(self)?;

        // If there are no more open tags then write the </plist> element
        if self.stack.is_empty() {
            // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it
            // about the </plist> tag as well.
            self.xml_writer
                .inner_mut()
                .write_all(b"\n</plist>")
                .map_err(error::from_io_without_position)?;
            self.xml_writer
                .inner_mut()
                .flush()
                .map_err(error::from_io_without_position)?;
        }

        Ok(())
    }

    fn write_value_event<F: FnOnce(&mut Self) -> Result<(), Error>>(
        &mut self,
        event_kind: EventKind,
        f: F,
    ) -> Result<(), Error> {
        self.write_event(|this| {
            if this.expecting_key {
                return Err(ErrorKind::UnexpectedEventType {
                    expected: EventKind::DictionaryKeyOrEndCollection,
                    found: event_kind,
                }
                .without_position());
            }
            f(this)?;
            this.expecting_key = this.stack.last() == Some(&Element::Dictionary);
            Ok(())
        })
    }
}

impl<W: Write> Writer for XmlWriter<W> {
    fn write_start_array(&mut self, _len: Option<u64>) -> Result<(), Error> {
        self.write_value_event(EventKind::StartArray, |this| {
            this.start_element("array")?;
            this.stack.push(Element::Array);
            Ok(())
        })
    }

    fn write_start_dictionary(&mut self, _len: Option<u64>) -> Result<(), Error> {
        self.write_value_event(EventKind::StartDictionary, |this| {
            this.start_element("dict")?;
            this.stack.push(Element::Dictionary);
            Ok(())
        })
    }

    fn write_end_collection(&mut self) -> Result<(), Error> {
        self.write_event(|this| {
            match (this.stack.pop(), this.expecting_key) {
                (Some(Element::Dictionary), true) => {
                    this.end_element("dict")?;
                }
                (Some(Element::Array), _) => {
                    this.end_element("array")?;
                }
                (Some(Element::Dictionary), false) | (None, _) => {
                    return Err(ErrorKind::UnexpectedEventType {
                        expected: EventKind::ValueOrStartCollection,
                        found: EventKind::EndCollection,
                    }
                    .without_position());
                }
            }
            this.expecting_key = this.stack.last() == Some(&Element::Dictionary);
            Ok(())
        })
    }

    fn write_boolean(&mut self, value: bool) -> Result<(), Error> {
        self.write_value_event(EventKind::Boolean, |this| {
            let value_str = if value { "true" } else { "false" };
            this.start_element(value_str)?;
            this.end_element(value_str)
        })
    }

    fn write_data(&mut self, value: &[u8]) -> Result<(), Error> {
        self.write_value_event(EventKind::Data, |this| {
            let base64_data = base64_encode_plist(&value, this.stack.len());
            this.write_element_and_value("data", &base64_data)
        })
    }

    fn write_date(&mut self, value: Date) -> Result<(), Error> {
        self.write_value_event(EventKind::Date, |this| {
            this.write_element_and_value("date", &value.to_xml_format())
        })
    }

    fn write_integer(&mut self, value: Integer) -> Result<(), Error> {
        self.write_value_event(EventKind::Integer, |this| {
            this.write_element_and_value("integer", &value.to_string())
        })
    }

    fn write_real(&mut self, value: f64) -> Result<(), Error> {
        self.write_value_event(EventKind::Real, |this| {
            this.write_element_and_value("real", &value.to_string())
        })
    }

    fn write_string(&mut self, value: &str) -> Result<(), Error> {
        self.write_event(|this| {
            if this.expecting_key {
                this.write_element_and_value("key", &*value)?;
                this.expecting_key = false;
            } else {
                this.write_element_and_value("string", &*value)?;
                this.expecting_key = this.stack.last() == Some(&Element::Dictionary);
            }
            Ok(())
        })
    }

    fn write_uid(&mut self, _value: Uid) -> Result<(), Error> {
        Err(ErrorKind::UidNotSupportedInXmlPlist.without_position())
    }
}

pub(crate) fn from_xml_error(err: XmlWriterError) -> Error {
    match err {
        XmlWriterError::Io(err) => ErrorKind::Io(err).without_position(),
        XmlWriterError::DocumentStartAlreadyEmitted
        | XmlWriterError::LastElementNameNotAvailable
        | XmlWriterError::EndElementNameIsNotEqualToLastStartElementName
        | XmlWriterError::EndElementNameIsNotSpecified => unreachable!(),
    }
}

fn base64_encode_plist(data: &[u8], indent: usize) -> String {
    // XML plist data elements are always formatted by apple tools as
    // <data>
    // AAAA..AA (68 characters per line)
    // </data>
    // Allocate space for base 64 string and line endings up front
    const LINE_LEN: usize = 68;
    let mut line_ending = Vec::with_capacity(1 + indent);
    line_ending.push(b'\n');
    (0..indent).for_each(|_| line_ending.push(b'\t'));

    // Find the max length of `data` encoded as a base 64 string with padding
    let base64_max_string_len = data.len() * 4 / 3 + 4;

    // Find the max length of the formatted base 64 string as: max length of the base 64 string
    // + line endings and indents at the start of the string and after every line
    let base64_max_string_len_with_formatting =
        base64_max_string_len + (2 + base64_max_string_len / LINE_LEN) * line_ending.len();

    let mut output = vec![0; base64_max_string_len_with_formatting];

    // Start output with a line ending and indent
    output[..line_ending.len()].copy_from_slice(&line_ending);

    // Encode `data` as a base 64 string
    let base64_string_len =
        base64::encode_config_slice(data, base64::STANDARD, &mut output[line_ending.len()..]);

    // Line wrap the base 64 encoded string
    let line_wrap_len = line_wrap::line_wrap(
        &mut output[line_ending.len()..],
        base64_string_len,
        LINE_LEN,
        &line_wrap::SliceLineEnding::new(&line_ending),
    );

    // Add the final line ending and indent
    output[line_ending.len() + base64_string_len + line_wrap_len..][..line_ending.len()]
        .copy_from_slice(&line_ending);

    // Ensure output is the correct length
    output.truncate(base64_string_len + line_wrap_len + 2 * line_ending.len());
    String::from_utf8(output).expect("base 64 string must be valid utf8")
}

#[cfg(test)]
mod tests {
    use std::io::Cursor;

    use super::*;
    use crate::stream::Event;

    #[test]
    fn streaming_parser() {
        let plist = &[
            Event::StartDictionary(None),
            Event::String("Author".into()),
            Event::String("William Shakespeare".into()),
            Event::String("Lines".into()),
            Event::StartArray(None),
            Event::String("It is a tale told by an idiot,".into()),
            Event::String("Full of sound and fury, signifying nothing.".into()),
            Event::Data((0..128).collect::<Vec<_>>().into()),
            Event::EndCollection,
            Event::String("Death".into()),
            Event::Integer(1564.into()),
            Event::String("Height".into()),
            Event::Real(1.60),
            Event::String("Data".into()),
            Event::Data(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0].into()),
            Event::String("Birthdate".into()),
            Event::Date(super::Date::from_xml_format("1981-05-16T11:32:06Z").unwrap()),
            Event::String("Comment".into()),
            Event::String("2 < 3".into()), // make sure characters are escaped
            Event::String("BiggestNumber".into()),
            Event::Integer(18446744073709551615u64.into()),
            Event::String("SmallestNumber".into()),
            Event::Integer((-9223372036854775808i64).into()),
            Event::String("IsTrue".into()),
            Event::Boolean(true),
            Event::String("IsNotFalse".into()),
            Event::Boolean(false),
            Event::EndCollection,
        ];

        let expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
\t<key>Author</key>
\t<string>William Shakespeare</string>
\t<key>Lines</key>
\t<array>
\t\t<string>It is a tale told by an idiot,</string>
\t\t<string>Full of sound and fury, signifying nothing.</string>
\t\t<data>
\t\tAAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy
\t\tMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2Rl
\t\tZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=
\t\t</data>
\t</array>
\t<key>Death</key>
\t<integer>1564</integer>
\t<key>Height</key>
\t<real>1.6</real>
\t<key>Data</key>
\t<data>
\tAAAAvgAAAAMAAAAeAAAA
\t</data>
\t<key>Birthdate</key>
\t<date>1981-05-16T11:32:06Z</date>
\t<key>Comment</key>
\t<string>2 < 3</string>
\t<key>BiggestNumber</key>
\t<integer>18446744073709551615</integer>
\t<key>SmallestNumber</key>
\t<integer>-9223372036854775808</integer>
\t<key>IsTrue</key>
\t<true/>
\t<key>IsNotFalse</key>
\t<false/>
</dict>
</plist>";

        let actual = events_to_xml(plist, XmlWriteOptions::default());

        assert_eq!(actual, expected);
    }

    #[test]
    fn custom_indent_string() {
        let plist = &[
            Event::StartArray(None),
            Event::String("It is a tale told by an idiot,".into()),
            Event::String("Full of sound and fury, signifying nothing.".into()),
            Event::EndCollection,
        ];

        let expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<array>
.<string>It is a tale told by an idiot,</string>
.<string>Full of sound and fury, signifying nothing.</string>
</array>
</plist>";

        let actual = events_to_xml(plist, XmlWriteOptions::default().indent_string("."));

        assert_eq!(actual, expected);
    }

    fn events_to_xml<'a>(
        events: impl IntoIterator<Item = &'a Event<'a>>,
        options: XmlWriteOptions,
    ) -> String {
        let mut cursor = Cursor::new(Vec::new());
        let mut writer = XmlWriter::new_with_options(&mut cursor, &options);
        for event in events {
            writer.write(event).unwrap();
        }
        String::from_utf8(cursor.into_inner()).unwrap()
    }
}
<|endoftext|>


<commit_before>// XXX(scode): Consider supporting cloning/snapshotting metrics.
// XXX(scode): Consider whether we should expose the current values in the public traits.
// XXX(scode): Missing histograms.
// XXX(scode): Missing meters (questionable).
// XXX(scode): Transition to lock-less where possible.

use std::num::Int;
use std::sync::Arc;
use std::sync::Mutex;

/// A 64 bit signed counter.
pub trait Counter<T: Int> {
    /// Increment the counter by the given delta.
    ///
    // The delta may be negative (for types T where this is possible).
    fn inc(&mut self, delta: T);

    /// Decrement the counter by the given delta.
    ///
    /// The delta may be negative (for types T where this is possible).
    ///
    /// dec(n) is equivalent of dec(-n)
    fn dec(&mut self, delta: T);
}

/// A gauge has a single value at any given moment in time, and can only be
/// updated by providing an entirely new value to replace the existing one
/// (if any).
pub trait Gauge<T> {
    fn set(&mut self, value: T);
}

pub struct SimpleCounter<T: Int> {
    value: T,
}

impl<T: Int> Counter<T> for SimpleCounter<T> {
    fn inc(&mut self, delta: T) {
        self.value = self.value + delta;
    }

    fn dec(&mut self, delta: T) {
        self.value = self.value - delta;
    }
}

pub struct SharedCounter<T: Int + Send> {
    value: Arc<Mutex<T>>,
}

impl<T: Int + Send> Counter<T> for SharedCounter<T> {
    fn inc(&mut self, delta: T) {
        let mut value = self.value.lock().unwrap();

        *value = *value + delta;
    }

    fn dec(&mut self, delta: T) {
        let mut value = self.value.lock().unwrap();

        *value = *value - delta;
    }
}

pub struct SimpleGauge<T> {
    value: Option<T>,
}

impl<T> Gauge<T> for SimpleGauge<T> {
    fn set(&mut self, new_value: T) {
        self.value = Some(new_value);
    }
}

pub struct SharedGauge<T: Send> {
    value: Arc<Mutex<Option<T>>>,
}

impl<T: Send> Gauge<T> for SharedGauge<T> {
    fn set(&mut self, new_value: T) {
        *self.value.lock().unwrap() = Some(new_value)
    }
}

#[cfg(test)]
mod test {
    // TODO(scode): Add tests.
}

<commit_msg>Partial test of metrics, some API tweaks.<commit_after>// XXX(scode): Consider supporting cloning/snapshotting metrics.
// XXX(scode): Missing histograms.
// XXX(scode): Missing meters (questionable).
// XXX(scode): Transition to lock-less where possible.

use std::num::Int;
use std::sync::Arc;
use std::sync::Mutex;

/// A 64 bit signed counter.
pub trait Counter<T: Int> {
    /// Increment the counter by the given delta.
    ///
    // The delta may be negative (for types T where this is possible).
    fn inc(&mut self, delta: T);

    /// Decrement the counter by the given delta.
    ///
    /// The delta may be negative (for types T where this is possible).
    ///
    /// dec(n) is equivalent of dec(-n)
    fn dec(&mut self, delta: T);

    /// Return the current value of the counter.
    fn get(&self) -> T;
}

/// A gauge has a single value at any given moment in time, and can only be
/// updated by providing an entirely new value.
pub trait Gauge<T: Clone> {
    fn set(&mut self, value: Option<T>);
    fn get(&self) -> Option<T>;
}

pub struct SimpleCounter<T: Int> {
    value: T,
}

impl<T: Int> Counter<T> for SimpleCounter<T> {
    fn inc(&mut self, delta: T) {
        self.value = self.value + delta;
    }

    fn dec(&mut self, delta: T) {
        self.value = self.value - delta;
    }

    fn get(&self) -> T {
        self.value
    }
}

pub struct SharedCounter<T: Int + Send> {
    value: Arc<Mutex<T>>,
}

impl<T: Int + Send> Counter<T> for SharedCounter<T> {
    fn inc(&mut self, delta: T) {
        let mut value = self.value.lock().unwrap();

        *value = *value + delta;
    }

    fn dec(&mut self, delta: T) {
        let mut value = self.value.lock().unwrap();

        *value = *value - delta;
    }

    fn get(&self) -> T {
        let value = self.value.lock().unwrap();

        *value
    }
}

pub struct SimpleGauge<T: Clone> {
    value: Option<T>,
}

impl<T: Clone> Gauge<T> for SimpleGauge<T> {
    fn set(&mut self, new_value: Option<T>) {
        self.value = new_value;
    }

    fn get(&self) -> Option<T> {
        self.value.clone()
    }
}

pub struct SharedGauge<T: Send> {
    value: Arc<Mutex<Option<T>>>,
}

impl<T: Clone + Send> Gauge<T> for SharedGauge<T> {
    fn set(&mut self, new_value: Option<T>) {
        *self.value.lock().unwrap() = new_value;
    }

    fn get(&self) -> Option<T> {
        (*self.value.lock().unwrap()).clone()
    }
}

#[cfg(test)]
mod test {
    use std::sync::Arc;
    use std::sync::Mutex;

    #[test]
    fn test_counters() {
        use metrics::Counter;
        use metrics::SharedCounter;
        use metrics::SimpleCounter;

        let mut counters = Vec::<Box<Counter<i64>>>::new();
        counters.push(Box::new(SimpleCounter { value: 0i64, }));
        counters.push(Box::new(SharedCounter { value: Arc::new(Mutex::new(0i64)) }));

        for mut c in counters {
            assert_eq!(c.get(), 0);
            c.inc(1);
            assert_eq!(c.get(), 1);
            c.dec(1);
            assert_eq!(c.get(), 0);

            c.dec(-1);
            assert_eq!(c.get(), 1);
            c.inc(-1);
            assert_eq!(c.get(), 0);
        }
    }

    // TODO(scode): Test gauge.
}

<|endoftext|>


<commit_before>#![feature(test)]

extern crate abomonation;
extern crate test;

use abomonation::*;
use test::Bencher;
use std::io::Read;

#[bench] fn enc_empty(bencher: &mut Bencher) { _bench_enc(bencher, vec![(); 1024]); }
#[bench] fn dec_empty(bencher: &mut Bencher) { _bench_dec(bencher, vec![(); 1024]); }

#[bench] fn enc_u64(bencher: &mut Bencher) { _bench_enc(bencher, vec![0u64; 1024]); }
#[bench] fn dec_u64(bencher: &mut Bencher) { _bench_dec(bencher, vec![0u64; 1024]); }

#[bench] fn enc_u8_u64(bencher: &mut Bencher) { _bench_enc(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn dec_u8_u64(bencher: &mut Bencher) { _bench_dec(bencher, vec![(0u8, 0u64); 512]); }

#[bench] fn enc_string10(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn dec_string10(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!"); 1024]); }

#[bench] fn enc_string20(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn dec_string20(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }

#[bench] fn enc_vec_u_s(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn dec_vec_u_s(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn own_vec_u_s(bencher: &mut Bencher) { _bench_own(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

#[bench] fn enc_vec_u_vn_s(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn dec_vec_u_vn_s(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

fn _bench_enc<T: Abomonation>(bencher: &mut Bencher, record: T) {

    // prepare encoded data for bencher.bytes
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // repeatedly encode this many bytes
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        bytes.clear();
        encode(&record, &mut bytes);
    });
}

fn _bench_dec<T: Abomonation+Eq>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        let result = decode::<T>(&mut bytes).unwrap().0;
        assert!(&record == result);
    });
}

fn _bench_own<T: Abomonation+Eq+Clone>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        let result = (*decode::<T>(&mut bytes[..]).unwrap().0).clone();
        assert!(record == result);
    });
}
<commit_msg>removed validation from decode benches; fleshed out ownership benches<commit_after>#![feature(test)]

extern crate abomonation;
extern crate test;

use abomonation::*;
use test::Bencher;
use std::io::Read;

#[bench] fn empty_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![(); 1024]); }
#[bench] fn empty_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![(); 1024]); }
#[bench] fn empty_own(bencher: &mut Bencher) { _bench_own(bencher, vec![(); 1024]); }

#[bench] fn u64_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![0u64; 1024]); }
#[bench] fn u64_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![0u64; 1024]); }
#[bench] fn u64_own(bencher: &mut Bencher) { _bench_own(bencher, vec![0u64; 1024]); }

#[bench] fn u8_u64_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn u8_u64_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![(0u8, 0u64); 512]); }
#[bench] fn u8_u64_own(bencher: &mut Bencher) { _bench_own(bencher, vec![(0u8, 0u64); 512]); }

#[bench] fn string10_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string10_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!"); 1024]); }
#[bench] fn string10_own(bencher: &mut Bencher) { _bench_own(bencher, vec![format!("grawwwwrr!"); 1024]); }

#[bench] fn string20_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn string20_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }
#[bench] fn string20_own(bencher: &mut Bencher) { _bench_own(bencher, vec![format!("grawwwwrr!!!!!!!!!!!"); 512]); }

#[bench] fn vec_u_s_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_s_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_s_own(bencher: &mut Bencher) { _bench_own(bencher, vec![vec![(0u64, format!("grawwwwrr!")); 32]; 32]); }

#[bench] fn vec_u_vn_s_enc(bencher: &mut Bencher) { _bench_enc(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_vn_s_dec(bencher: &mut Bencher) { _bench_dec(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }
#[bench] fn vec_u_vn_s_own(bencher: &mut Bencher) { _bench_own(bencher, vec![vec![(0u64, vec![(); 1 << 40], format!("grawwwwrr!")); 32]; 32]); }

fn _bench_enc<T: Abomonation>(bencher: &mut Bencher, record: T) {

    // prepare encoded data for bencher.bytes
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // repeatedly encode this many bytes
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        bytes.clear();
        encode(&record, &mut bytes);
    });
}

fn _bench_dec<T: Abomonation+Eq>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        decode::<T>(&mut bytes).is_ok()
        // assert!(&record == result);
    });
}

fn _bench_own<T: Abomonation+Eq+Clone>(bencher: &mut Bencher, record: T) {

    // prepare encoded data
    let mut bytes = Vec::new();
    encode(&record, &mut bytes);

    // repeatedly decode (and validate)
    bencher.bytes = bytes.len() as u64;
    bencher.iter(|| {
        (*decode::<T>(&mut bytes[..]).unwrap().0).clone()
        // assert!(record == result);
    });
}
<|endoftext|>


<commit_before>use {
    bmfont::*,
    criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion},
    std::fs::File,
};

const INPUTS: &[(&str, &str)] = &[
    ("01: Empty", ""),
    ("02: Character", "w"),
    ("03: Word", "Score"),
    ("04: Pair", "Winner: Attackgoat"),
    ("05: Sentance", "The lazy dog can sit wherever she wishes."),
    ("06: Two lines", "Where is her sweater?\nIt's not in her car."),
    ("07: Long sentance", "The acquisition of wealth is no longer the driving force of our lives. We work to better ourselves and the rest of humanity."),
    ("08: Two long lines", "Someone once told me that time was a predator that stalked us all our lives. I rather believe that time is a\ncompanion who goes with us on the journey and reminds us to cherish every moment, because it will never come again. What we leave behind is not as important as how we've lived."),
    ("09: Lyrics", "Van Gogh my earlobe\nI can't hear, I'm here though\nI may be a weirdo, but this is my year, yo\nMy life may be crazy\nMy lack of the lazy has let me write code that I love on the daily.\nVan Gogh my earlobe\nI can't hear, I'm here though\nI may be a weirdo, but this is my year, yo\nMy life may be crazy\nMy lack of the lazy has let me write code that I love on the daily.\nVan Gogh my earlobe\nI can't hear, I'm here though\nI may be a weirdo, but this is my year, yo\nMy life may be crazy\nMy lack of the lazy has let me write code that I love on the daily."),
];

fn parse(c: &mut Criterion) {
    let file = File::open("font.fnt").unwrap();
    let font = BMFont::new(file, OrdinateOrientation::TopToBottom).unwrap();
    let mut group = c.benchmark_group("Parse");
    for (desc, input) in INPUTS.iter() {
        group.bench_with_input(BenchmarkId::new("Input", desc), input, |b, input| {
            b.iter(|| font.parse(black_box(input)))
        });
    }
}

criterion_group!(benches, parse);
criterion_main!(benches);
<commit_msg>Spelling fix<commit_after>use {
    bmfont::*,
    criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion},
    std::fs::File,
};

const INPUTS: &[(&str, &str)] = &[
    ("01: Empty", ""),
    ("02: Character", "w"),
    ("03: Word", "Score"),
    ("04: Pair", "Winner: Attackgoat"),
    ("05: Sentence", "The lazy dog can sit wherever she wishes."),
    ("06: Two lines", "Where is her sweater?\nIt's not in her car."),
    ("07: Long sentence", "The acquisition of wealth is no longer the driving force of our lives. We work to better ourselves and the rest of humanity."),
    ("08: Two long lines", "Someone once told me that time was a predator that stalked us all our lives. I rather believe that time is a\ncompanion who goes with us on the journey and reminds us to cherish every moment, because it will never come again. What we leave behind is not as important as how we've lived."),
    ("09: Lyrics", "Van Gogh my earlobe\nI can't hear, I'm here though\nI may be a weirdo, but this is my year, yo\nMy life may be crazy\nMy lack of the lazy has let me write code that I love on the daily.\nVan Gogh my earlobe\nI can't hear, I'm here though\nI may be a weirdo, but this is my year, yo\nMy life may be crazy\nMy lack of the lazy has let me write code that I love on the daily.\nVan Gogh my earlobe\nI can't hear, I'm here though\nI may be a weirdo, but this is my year, yo\nMy life may be crazy\nMy lack of the lazy has let me write code that I love on the daily."),
];

fn parse(c: &mut Criterion) {
    let file = File::open("font.fnt").unwrap();
    let font = BMFont::new(file, OrdinateOrientation::TopToBottom).unwrap();
    let mut group = c.benchmark_group("Parse");
    for (desc, input) in INPUTS.iter() {
        group.bench_with_input(BenchmarkId::new("Input", desc), input, |b, input| {
            b.iter(|| font.parse(black_box(input)))
        });
    }
}

criterion_group!(benches, parse);
criterion_main!(benches);
<|endoftext|>


<commit_before>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// 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 hyper::status::StatusCode;
use utils::say::{say, sayln};
use errors::{DeliveryError, Kind};
use std::path::{PathBuf};
use http::APIClient;
use git;
use config::Config;

pub fn import(config: &Config, path: &PathBuf) -> Result<(), DeliveryError> {
    let org = try!(config.organization());
    let proj = try!(config.project());

    // Init && config local repo if necessary
    try!(git::init_repo(path));
    let url = try!(config.delivery_git_ssh_url());
    if try!(git::config_repo(&url, path)) {
        sayln("white", "Remote 'delivery' added to git config!");
    } else {
        sayln("red", "Remote named 'delivery' already exists - not modifying");
    }

    let client = try!(APIClient::from_config(config));

    if ! client.project_exists(&org, &proj) {
        say("white", "Creating project: ");
        sayln("magenta", &format!("{} ", proj));
        let _ = client.create_project(&org, &proj);
    } else {
        say("white", "Project ");
        say("magenta", &format!("{} ", proj));
        sayln("white", "already exists.");
    }

    say("white", "Checking for content on the git remote ");
    say("magenta", "delivery: ");
    if git::server_content() {
        sayln("red", "Found commits upstream, not pushing local commits.");
    } else {
        sayln("white", "No upstream content; pushing local content to server.");
        let _ = git::git_push_master();
    }

    say("white", "Creating master pipeline for project: ");
    say("magenta", &format!("{} ", proj));
    say("white", "... ");
    match client.create_pipeline(&org, &proj, "master") {
         Ok(_) => {
            sayln("white", "done");
        },
        Err(e) => {
            match e {
                Kind::ApiError(StatusCode::Conflict, _) => {
                    sayln("white", " already exists.");
                },
                Kind::ApiError(code, Ok(msg)) => {
                    sayln("red", &format!("{} {}", code, msg));
                },
                _ => {
                    sayln("red", &format!("Other error: {:?}", e));
                }
            }
        }
    }
    return Ok(())
}

<commit_msg>Extract API error handing/messaging into function<commit_after>//
// Copyright:: Copyright (c) 2015 Chef Software, Inc.
// License:: Apache License, Version 2.0
//
// 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 hyper::client::response::Response;
use hyper::status::StatusCode;
use utils::say::{say, sayln};
use errors::{DeliveryError, Kind};
use std::path::{PathBuf};
use http::APIClient;
use git;
use config::Config;

fn call_api<F>(closure: F) where F : Fn() -> Result<Response, Kind> {
    match closure() {
        Ok(_) => {
            sayln("white", "done");
        },
        Err(e) => {
            match e {
                Kind::ApiError(StatusCode::Conflict, _) => {
                    sayln("white", " already exists.");
                },
                Kind::ApiError(code, Ok(msg)) => {
                    sayln("red", &format!("{} {}", code, msg));
                },
                _ => {
                    sayln("red", &format!("Other error: {:?}", e));
                }
            }
        }
    }
}

pub fn import(config: &Config, path: &PathBuf) -> Result<(), DeliveryError> {
    let org = try!(config.organization());
    let proj = try!(config.project());

    // Init && config local repo if necessary
    try!(git::init_repo(path));
    let url = try!(config.delivery_git_ssh_url());
    if try!(git::config_repo(&url, path)) {
        sayln("white", "Remote 'delivery' added to git config!");
    } else {
        sayln("red", "Remote named 'delivery' already exists - not modifying");
    }

    let client = try!(APIClient::from_config(config));

    if ! client.project_exists(&org, &proj) {
        say("white", "Creating project: ");
        sayln("magenta", &format!("{} ", proj));
        let _ = client.create_project(&org, &proj);
    } else {
        say("white", "Project ");
        say("magenta", &format!("{} ", proj));
        sayln("white", "already exists.");
    }

    say("white", "Checking for content on the git remote ");
    say("magenta", "delivery: ");
    if git::server_content() {
        sayln("red", "Found commits upstream, not pushing local commits.");
    } else {
        sayln("white", "No upstream content; pushing local content to server.");
        let _ = git::git_push_master();
    }

    say("white", "Creating master pipeline for project: ");
    say("magenta", &format!("{} ", proj));
    say("white", "... ");
    call_api(|| client.create_pipeline(&org, &proj, "master"));
    return Ok(())
}

<|endoftext|>


<commit_before>extern crate fuse;
extern crate libc;
extern crate time;

use std::io::{TypeFile, TypeDirectory, UserFile, UserDir};
use std::os;
use libc::ENOENT;
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyData, ReplyEntry, ReplyAttr, ReplyDirectory};

static TTL: Timespec = Timespec { sec: 1, nsec: 0 };					// 1 second

static CREATE_TIME: Timespec = Timespec { sec: 1381237736, nsec: 0 };	// 2013-10-08 08:56

static HELLO_DIR_ATTR: FileAttr = FileAttr {
	ino: 1,
	size: 0,
	blocks: 0,
	atime: CREATE_TIME,
	mtime: CREATE_TIME,
	ctime: CREATE_TIME,
	crtime: CREATE_TIME,
	kind: TypeDirectory,
	perm: UserDir,
	nlink: 2,
	uid: 501,
	gid: 20,
	rdev: 0,
	flags: 0,
};

static HELLO_TXT_CONTENT: &'static str = "Hello World!\n";

static HELLO_TXT_ATTR: FileAttr = FileAttr {
	ino: 2,
	size: 13,
	blocks: 1,
	atime: CREATE_TIME,
	mtime: CREATE_TIME,
	ctime: CREATE_TIME,
	crtime: CREATE_TIME,
	kind: TypeFile,
	perm: UserFile,
	nlink: 1,
	uid: 501,
	gid: 20,
	rdev: 0,
	flags: 0,
};

struct HelloFS;

impl Filesystem for HelloFS {
	fn lookup (&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
		if parent == 1 && name.as_str() == Some("hello.txt") {
			reply.entry(&TTL, &HELLO_TXT_ATTR, 0);
		} else {
			reply.error(ENOENT);
		}
	}

	fn getattr (&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
		match ino {
			1 => reply.attr(&TTL, &HELLO_DIR_ATTR),
			2 => reply.attr(&TTL, &HELLO_TXT_ATTR),
			_ => reply.error(ENOENT),
		}
	}

	fn read (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, _size: uint, reply: ReplyData) {
		if ino == 2 {
			reply.data(HELLO_TXT_CONTENT.as_bytes().slice_from(offset as uint));
		} else {
			reply.error(ENOENT);
		}
	}

	fn readdir (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, mut reply: ReplyDirectory) {
		if ino == 1 {
			if offset == 0 {
				reply.add(1, 0, TypeDirectory, &PosixPath::new("."));
				reply.add(1, 1, TypeDirectory, &PosixPath::new(".."));
				reply.add(2, 2, TypeFile, &PosixPath::new("hello.txt"));
			}
			reply.ok();
		} else {
			reply.error(ENOENT);
		}
	}
}

fn main () {
	let mountpoint = Path::new(os::args()[1].as_slice());
	fuse::mount(HelloFS, &mountpoint, []);
}
<commit_msg>Switch to io::{USER_FILE,USER_DIR}<commit_after>extern crate fuse;
extern crate libc;
extern crate time;

use std::io::{TypeFile, TypeDirectory, USER_FILE, USER_DIR};
use std::os;
use libc::ENOENT;
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyData, ReplyEntry, ReplyAttr, ReplyDirectory};

static TTL: Timespec = Timespec { sec: 1, nsec: 0 };					// 1 second

static CREATE_TIME: Timespec = Timespec { sec: 1381237736, nsec: 0 };	// 2013-10-08 08:56

static HELLO_DIR_ATTR: FileAttr = FileAttr {
	ino: 1,
	size: 0,
	blocks: 0,
	atime: CREATE_TIME,
	mtime: CREATE_TIME,
	ctime: CREATE_TIME,
	crtime: CREATE_TIME,
	kind: TypeDirectory,
	perm: USER_DIR,
	nlink: 2,
	uid: 501,
	gid: 20,
	rdev: 0,
	flags: 0,
};

static HELLO_TXT_CONTENT: &'static str = "Hello World!\n";

static HELLO_TXT_ATTR: FileAttr = FileAttr {
	ino: 2,
	size: 13,
	blocks: 1,
	atime: CREATE_TIME,
	mtime: CREATE_TIME,
	ctime: CREATE_TIME,
	crtime: CREATE_TIME,
	kind: TypeFile,
	perm: USER_FILE,
	nlink: 1,
	uid: 501,
	gid: 20,
	rdev: 0,
	flags: 0,
};

struct HelloFS;

impl Filesystem for HelloFS {
	fn lookup (&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
		if parent == 1 && name.as_str() == Some("hello.txt") {
			reply.entry(&TTL, &HELLO_TXT_ATTR, 0);
		} else {
			reply.error(ENOENT);
		}
	}

	fn getattr (&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
		match ino {
			1 => reply.attr(&TTL, &HELLO_DIR_ATTR),
			2 => reply.attr(&TTL, &HELLO_TXT_ATTR),
			_ => reply.error(ENOENT),
		}
	}

	fn read (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, _size: uint, reply: ReplyData) {
		if ino == 2 {
			reply.data(HELLO_TXT_CONTENT.as_bytes().slice_from(offset as uint));
		} else {
			reply.error(ENOENT);
		}
	}

	fn readdir (&mut self, _req: &Request, ino: u64, _fh: u64, offset: u64, mut reply: ReplyDirectory) {
		if ino == 1 {
			if offset == 0 {
				reply.add(1, 0, TypeDirectory, &PosixPath::new("."));
				reply.add(1, 1, TypeDirectory, &PosixPath::new(".."));
				reply.add(2, 2, TypeFile, &PosixPath::new("hello.txt"));
			}
			reply.ok();
		} else {
			reply.error(ENOENT);
		}
	}
}

fn main () {
	let mountpoint = Path::new(os::args()[1].as_slice());
	fuse::mount(HelloFS, &mountpoint, []);
}
<|endoftext|>