<commit_before>#[cfg(target_arch = "aarch64")]
use aarch64::regs::*;
use rayon::prelude::*;
use std::env;
use std::f64::consts::{E, PI};
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::str;
use std::thread;
use std::time::Instant;
use std::vec;
#[cfg(target_os = "linux")]
use syscalls::SYS_getpid;

mod laplace;
mod matmul;

pub use matmul::test_matmul_strassen;

pub fn thread_creation() -> Result<(), ()> {
	const N: usize = 10;

	// cache warmup
	{
		let builder = thread::Builder::new();
		let child = builder.spawn(|| {}).unwrap();
		let _ = child.join();
	}

	let now = Instant::now();
	for _ in 0..N {
		let builder = thread::Builder::new();
		let child = builder.spawn(|| {}).unwrap();
		let _ = child.join();
	}

	println!(
		"Time to create and to join a thread: {} ms",
		now.elapsed().as_secs_f64() * 1000.0f64 / f64::from(N as i32)
	);

	Ok(())
}

pub fn pi_sequential(num_steps: u64) -> Result<(), ()> {
	let step = 1.0 / num_steps as f64;
	let mut sum = 0 as f64;

	for i in 0..num_steps {
		let x = (i as f64 + 0.5) * step;
		sum += 4.0 / (1.0 + x * x);
	}

	let mypi = sum * (1.0 / num_steps as f64);
	println!("Pi: {} (sequential)", mypi);

	if (mypi - PI).abs() < 0.00001 {
		Ok(())
	} else {
		Err(())
	}
}

pub fn pi_parallel(num_steps: u64) -> Result<(), ()> {
	let ncpus = num_cpus::get();
	let pool = rayon::ThreadPoolBuilder::new()
		.num_threads(ncpus)
		.build()
		.unwrap();
	let step = 1.0 / num_steps as f64;

	let sum: f64 = pool.install(|| {
		(0..num_steps)
			.into_par_iter()
			.map(|i| {
				let x = (i as f64 + 0.5) * step;
				4.0 / (1.0 + x * x)
			})
			.sum()
	});

	let mypi = sum * (1.0 / num_steps as f64);
	println!("Pi: {} (with {} threads)", mypi, ncpus);

	if (mypi - PI).abs() < 0.00001 {
		Ok(())
	} else {
		Err(())
	}
}

pub fn read_file() -> Result<(), std::io::Error> {
	let mut file = File::open("/etc/hostname")?;
	let mut contents = String::new();
	file.read_to_string(&mut contents)?;

	println!("Hostname: {}", contents);

	Ok(())
}

pub fn create_file() -> Result<(), std::io::Error> {
	{
		let mut file = File::create("/tmp/foo.txt")?;
		file.write_all(b"Hello, world!")?;
	}

	let contents = {
		let mut file = File::open("/tmp/foo.txt")?;
		let mut contents = String::new();
		file.read_to_string(&mut contents)?;
		contents
	};

	// delete temporary file
	std::fs::remove_file("/tmp/foo.txt")?;

	if contents == "Hello, world!" {
		Ok(())
	} else {
		let kind = std::io::ErrorKind::Other;
		Err(std::io::Error::from(kind))
	}
}

pub fn print_argv() -> Result<(), ()> {
	let args = env::args();

	// Prints each argument on a separate line
	for (i, argument) in args.enumerate() {
		println!("argument[{}] = {}", i, argument);
	}

	Ok(())
}

pub fn print_env() -> Result<(), ()> {
	let envs = env::vars();

	// We will iterate through the references to the element returned by
	// env::vars();
	for (key, value) in envs {
		println!("{}: {}", key, value);
	}

	Ok(())
}

pub fn hello() -> Result<(), ()> {
	println!("Hello, world!");
	println!("Привет мир!");
	println!("こんにちは世界");
	println!("你好，世界");
	println!("สวัสดีชาวโลก");
	println!("Chào thế giới");
	let crab = vec![0xF0 as u8, 0x9F as u8, 0xA6 as u8, 0x80 as u8];
	println!(
		"Crab emoji: {}",
		String::from_utf8(crab).unwrap_or_default()
	);

	Ok(())
}

pub fn arithmetic() -> Result<(), ()> {
	let x = ((std::thread::current().id().as_u64().get() * 3) % 10) as f64 * 3.41f64;
	let y: f64 = x.exp();
	let z: f64 = y.log(E);

	println!("x = {}, e^x = {}, ln(e^x) = {}", x, y, z);

	Ok(())
}

pub fn threading() -> Result<(), ()> {
	// Make a vector to hold the children which are spawned.
	let mut children = vec![];

	for i in 0..2 {
		// Spin up another thread
		children.push(thread::spawn(move || {
			println!("this is thread number {}", i);
		}));
	}

	for child in children {
		// Wait for the thread to finish. Returns a result.
		let _ = child.join();
	}

	Ok(())
}

pub fn laplace(size_x: usize, size_y: usize) -> Result<(), ()> {
	let ncpus = num_cpus::get();
	let pool = rayon::ThreadPoolBuilder::new()
		.num_threads(ncpus)
		.build()
		.unwrap();
	let matrix = matrix_setup(size_x, size_y);

	let now = Instant::now();
	let (iterations, res) = pool.install(|| laplace::compute(matrix, size_x, size_y));
	println!(
		"Time to solve {} s, iterations {}, residuum {}",
		now.elapsed().as_secs_f64(),
		iterations,
		res
	);

	if res < 0.01 {
		Ok(())
	} else {
		Err(())
	}
}

pub fn matrix_setup(size_x: usize, size_y: usize) -> vec::Vec<vec::Vec<f64>> {
	let mut matrix = vec![vec![0.0; size_x * size_y]; 2];

	// top row
	for x in 0..size_x {
		matrix[0][x] = 1.0;
		matrix[1][x] = 1.0;
	}

	// bottom row
	for x in 0..size_x {
		matrix[0][(size_y - 1) * size_x + x] = 1.0;
		matrix[1][(size_y - 1) * size_x + x] = 1.0;
	}

	// left row
	for y in 0..size_y {
		matrix[0][y * size_x] = 1.0;
		matrix[1][y * size_x] = 1.0;
	}

	// right row
	for y in 0..size_y {
		matrix[0][y * size_x + size_x - 1] = 1.0;
		matrix[1][y * size_x + size_x - 1] = 1.0;
	}

	matrix
}
<commit_msg>use the current directory to create temporary files<commit_after>#[cfg(target_arch = "aarch64")]
use aarch64::regs::*;
use rayon::prelude::*;
use std::env;
use std::f64::consts::{E, PI};
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::str;
use std::thread;
use std::time::Instant;
use std::vec;
#[cfg(target_os = "linux")]
use syscalls::SYS_getpid;

mod laplace;
mod matmul;

pub use matmul::test_matmul_strassen;

pub fn thread_creation() -> Result<(), ()> {
	const N: usize = 10;

	// cache warmup
	{
		let builder = thread::Builder::new();
		let child = builder.spawn(|| {}).unwrap();
		let _ = child.join();
	}

	let now = Instant::now();
	for _ in 0..N {
		let builder = thread::Builder::new();
		let child = builder.spawn(|| {}).unwrap();
		let _ = child.join();
	}

	println!(
		"Time to create and to join a thread: {} ms",
		now.elapsed().as_secs_f64() * 1000.0f64 / f64::from(N as i32)
	);

	Ok(())
}

pub fn pi_sequential(num_steps: u64) -> Result<(), ()> {
	let step = 1.0 / num_steps as f64;
	let mut sum = 0 as f64;

	for i in 0..num_steps {
		let x = (i as f64 + 0.5) * step;
		sum += 4.0 / (1.0 + x * x);
	}

	let mypi = sum * (1.0 / num_steps as f64);
	println!("Pi: {} (sequential)", mypi);

	if (mypi - PI).abs() < 0.00001 {
		Ok(())
	} else {
		Err(())
	}
}

pub fn pi_parallel(num_steps: u64) -> Result<(), ()> {
	let ncpus = num_cpus::get();
	let pool = rayon::ThreadPoolBuilder::new()
		.num_threads(ncpus)
		.build()
		.unwrap();
	let step = 1.0 / num_steps as f64;

	let sum: f64 = pool.install(|| {
		(0..num_steps)
			.into_par_iter()
			.map(|i| {
				let x = (i as f64 + 0.5) * step;
				4.0 / (1.0 + x * x)
			})
			.sum()
	});

	let mypi = sum * (1.0 / num_steps as f64);
	println!("Pi: {} (with {} threads)", mypi, ncpus);

	if (mypi - PI).abs() < 0.00001 {
		Ok(())
	} else {
		Err(())
	}
}

pub fn read_file() -> Result<(), std::io::Error> {
	let mut file = File::open("/etc/hostname")?;
	let mut contents = String::new();
	file.read_to_string(&mut contents)?;

	println!("Hostname: {}", contents);

	Ok(())
}

pub fn create_file() -> Result<(), std::io::Error> {
	{
		let mut file = File::create("foo.txt")?;
		file.write_all(b"Hello, world!")?;
	}

	let contents = {
		let mut file = File::open("foo.txt")?;
		let mut contents = String::new();
		file.read_to_string(&mut contents)?;
		contents
	};

	// delete temporary file
	std::fs::remove_file("foo.txt")?;

	if contents == "Hello, world!" {
		Ok(())
	} else {
		let kind = std::io::ErrorKind::Other;
		Err(std::io::Error::from(kind))
	}
}

pub fn print_argv() -> Result<(), ()> {
	let args = env::args();

	// Prints each argument on a separate line
	for (i, argument) in args.enumerate() {
		println!("argument[{}] = {}", i, argument);
	}

	Ok(())
}

pub fn print_env() -> Result<(), ()> {
	let envs = env::vars();

	// We will iterate through the references to the element returned by
	// env::vars();
	for (key, value) in envs {
		println!("{}: {}", key, value);
	}

	Ok(())
}

pub fn hello() -> Result<(), ()> {
	println!("Hello, world!");
	println!("Привет мир!");
	println!("こんにちは世界");
	println!("你好，世界");
	println!("สวัสดีชาวโลก");
	println!("Chào thế giới");
	let crab = vec![0xF0 as u8, 0x9F as u8, 0xA6 as u8, 0x80 as u8];
	println!(
		"Crab emoji: {}",
		String::from_utf8(crab).unwrap_or_default()
	);

	Ok(())
}

pub fn arithmetic() -> Result<(), ()> {
	let x = ((std::thread::current().id().as_u64().get() * 3) % 10) as f64 * 3.41f64;
	let y: f64 = x.exp();
	let z: f64 = y.log(E);

	println!("x = {}, e^x = {}, ln(e^x) = {}", x, y, z);

	Ok(())
}

pub fn threading() -> Result<(), ()> {
	// Make a vector to hold the children which are spawned.
	let mut children = vec![];

	for i in 0..2 {
		// Spin up another thread
		children.push(thread::spawn(move || {
			println!("this is thread number {}", i);
		}));
	}

	for child in children {
		// Wait for the thread to finish. Returns a result.
		let _ = child.join();
	}

	Ok(())
}

pub fn laplace(size_x: usize, size_y: usize) -> Result<(), ()> {
	let ncpus = num_cpus::get();
	let pool = rayon::ThreadPoolBuilder::new()
		.num_threads(ncpus)
		.build()
		.unwrap();
	let matrix = matrix_setup(size_x, size_y);

	let now = Instant::now();
	let (iterations, res) = pool.install(|| laplace::compute(matrix, size_x, size_y));
	println!(
		"Time to solve {} s, iterations {}, residuum {}",
		now.elapsed().as_secs_f64(),
		iterations,
		res
	);

	if res < 0.01 {
		Ok(())
	} else {
		Err(())
	}
}

pub fn matrix_setup(size_x: usize, size_y: usize) -> vec::Vec<vec::Vec<f64>> {
	let mut matrix = vec![vec![0.0; size_x * size_y]; 2];

	// top row
	for x in 0..size_x {
		matrix[0][x] = 1.0;
		matrix[1][x] = 1.0;
	}

	// bottom row
	for x in 0..size_x {
		matrix[0][(size_y - 1) * size_x + x] = 1.0;
		matrix[1][(size_y - 1) * size_x + x] = 1.0;
	}

	// left row
	for y in 0..size_y {
		matrix[0][y * size_x] = 1.0;
		matrix[1][y * size_x] = 1.0;
	}

	// right row
	for y in 0..size_y {
		matrix[0][y * size_x + size_x - 1] = 1.0;
		matrix[1][y * size_x + size_x - 1] = 1.0;
	}

	matrix
}
<|endoftext|>


<commit_before>use super::{child_properties, function, general, properties, signal, trait_impls};
use crate::{
    analysis::special_functions::Type,
    analysis::{
        self,
        rust_type::{rust_type, rust_type_full},
    },
    case::CaseExt,
    codegen::general::{version_condition_no_doc, version_condition_string},
    env::Env,
    library, nameutil,
    traits::IntoString,
};
use std::io::{Result, Write};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::object::Info,
    generate_display_trait: bool,
) -> Result<()> {
    general::start_comments(w, &env.config)?;
    general::uses(w, env, &analysis.imports, analysis.version)?;

    general::define_object_type(
        w,
        env,
        &analysis.name,
        &analysis.c_type,
        analysis.c_class_type.as_deref(),
        &analysis.get_type,
        analysis.is_interface,
        &analysis.supertypes,
    )?;

    if need_generate_inherent(analysis) {
        writeln!(w)?;
        write!(w, "impl {} {{", analysis.name)?;
        for func_analysis in &analysis.constructors() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for func_analysis in &analysis.methods() {
                function::generate(
                    w,
                    env,
                    func_analysis,
                    Some(&analysis.specials),
                    analysis.version,
                    false,
                    false,
                    1,
                )?;
            }

            for property in &analysis.properties {
                properties::generate(w, env, property, false, false, 1)?;
            }

            for child_property in &analysis.child_properties {
                child_properties::generate(w, env, child_property, false, false, 1)?;
            }
        }

        for func_analysis in &analysis.functions() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for signal_analysis in analysis
                .signals
                .iter()
                .chain(analysis.notify_signals.iter())
            {
                signal::generate(w, env, signal_analysis, false, false, 1)?;
            }
        }

        writeln!(w, "}}")?;

        general::declare_default_from_new(w, env, &analysis.name, &analysis.functions)?;
    }

    trait_impls::generate(
        w,
        env,
        &analysis.name,
        &analysis.functions,
        &analysis.specials,
        if need_generate_trait(analysis) {
            Some(&analysis.trait_name)
        } else {
            None
        },
        analysis.version,
    )?;

    if !analysis.builder_properties.is_empty() {
        writeln!(w)?;
        generate_builder(w, env, analysis)?;
    }

    if analysis.concurrency != library::Concurrency::None {
        writeln!(w)?;
    }

    match analysis.concurrency {
        library::Concurrency::Send | library::Concurrency::SendSync => {
            writeln!(w, "unsafe impl Send for {} {{}}", analysis.name)?;
        }
        library::Concurrency::SendUnique => {
            if env.namespaces.is_glib_crate {
                writeln!(w, "unsafe impl ::SendUnique for {} {{", analysis.name)?;
            } else {
                writeln!(w, "unsafe impl glib::SendUnique for {} {{", analysis.name)?;
            }

            writeln!(w, "    fn is_unique(&self) -> bool {{")?;
            writeln!(w, "        self.ref_count() == 1")?;
            writeln!(w, "    }}")?;

            writeln!(w, "}}")?;
        }
        _ => (),
    }

    if let library::Concurrency::SendSync = analysis.concurrency {
        writeln!(w, "unsafe impl Sync for {} {{}}", analysis.name)?;
    }

    if !analysis.final_type {
        writeln!(w)?;
        writeln!(
            w,
            "pub const NONE_{}: Option<&{}> = None;",
            analysis.name.to_snake().to_uppercase(),
            analysis.name
        )?;
    }

    if need_generate_trait(analysis) {
        writeln!(w)?;
        generate_trait(w, env, analysis)?;
    }

    if generate_display_trait && !analysis.specials.has_trait(Type::Display) {
        writeln!(w, "\nimpl fmt::Display for {} {{", analysis.name,)?;
        // Generate Display trait implementation.
        writeln!(
            w,
            "\tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
             \t\tf.write_str(\"{}\")\n\
             \t}}\n\
             }}",
            analysis.name
        )?;
    }

    Ok(())
}

// TODO: instead create a Vec<> inside the Builder instead of Options.
fn generate_builder(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    let mut methods = vec![];
    let mut properties = vec![];
    writeln!(w, "#[derive(Clone, Default)]")?;
    writeln!(w, "pub struct {}Builder {{", analysis.name)?;
    for property in &analysis.builder_properties {
        match rust_type(env, property.typ) {
            Ok(type_string) => {
                let type_string = match type_string.as_str() {
                    s if nameutil::is_gstring(s) => "String",
                    "Vec<GString>" | "Vec<glib::GString>" | "Vec<crate::GString>" => "Vec<String>",
                    typ => typ,
                };
                let mut param_type = rust_type_full(
                    env,
                    property.typ,
                    library::Nullable(false),
                    property.set_in_ref_mode,
                    library::ParameterScope::None,
                    library::Concurrency::None,
                )
                .into_string();
                let (param_type_override, bounds, conversion) = match &param_type[..] {
                    "&str" => (None, String::new(), ".to_string()"),
                    "&[&str]" => (Some("Vec<String>".to_string()), String::new(), ""),
                    _ if !property.bounds.is_empty() => {
                        let (bounds, _) = function::bounds(&property.bounds, &[], false, false);
                        let alias = property
                            .bounds
                            .get_parameter_alias_info(&property.name)
                            .map(|(alias, _)| format!("&{}", alias));
                        (alias, bounds, ".clone().upcast()")
                    }
                    typ if typ.starts_with('&') => (None, String::new(), ".clone()"),
                    _ => (None, String::new(), ""),
                };
                if let Some(param_type_override) = param_type_override {
                    param_type = param_type_override.to_string();
                }
                let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
                let version_condition_string =
                    version_condition_string(env, property.version, false, 1);
                if let Some(ref version_condition_string) = version_condition_string {
                    writeln!(w, "{}", version_condition_string)?;
                }
                writeln!(w, "    {}: Option<{}>,", name, type_string)?;
                let prefix = version_condition_string
                    .map(|version| format!("{}\n", version))
                    .unwrap_or_default();
                methods.push(format!(
                    "\n{prefix}    pub fn {name}{bounds}(mut self, {name}: {param_type}) -> Self {{
        self.{name} = Some({name}{conversion});
        self
    }}",
                    prefix = prefix,
                    param_type = param_type,
                    name = name,
                    conversion = conversion,
                    bounds = bounds
                ));
                properties.push(property);
            }
            Err(_) => writeln!(w, "    //{}: /*Unknown type*/,", property.name)?,
        }
    }
    writeln!(
        w,
        "}}

impl {}Builder {{
    pub fn new() -> Self {{
        Self::default()
    }}
",
        analysis.name
    )?;

    writeln!(
        w,
        "
    pub fn build(self) -> {} {{
        let mut properties: Vec<(&str, &dyn ToValue)> = vec![];",
        analysis.name
    )?;
    for property in &properties {
        let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
        version_condition_no_doc(w, env, property.version, false, 2)?;
        writeln!(
            w,
            "\
            if let Some(ref {field}) = self.{field} {{
                properties.push((\"{name}\", {field}));
            }}",
            name = property.name,
            field = name
        )?;
    }
    let glib_crate_name = if env.namespaces.is_glib_crate {
        "crate"
    } else {
        "glib"
    };
    writeln!(
        w,
        r#"        let ret = {}::Object::new::<{}>(&properties)
            .expect("object new");"#,
        glib_crate_name, analysis.name
    )?;
    if let Some(code) = analysis.builder_postprocess.as_ref() {
        writeln!(w, "        {{\n            {}\n        }}", code)?;
    }
    writeln!(w, "    ret\n    }}")?;
    for method in methods {
        writeln!(w, "{}", method)?;
    }
    writeln!(w, "}}")
}

fn generate_trait(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    write!(w, "pub trait {}: 'static {{", analysis.trait_name)?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            true,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, true, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, true, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, true, 1)?;
    }
    writeln!(w, "}}")?;

    writeln!(w)?;
    write!(
        w,
        "impl<O: IsA<{}>> {} for O {{",
        analysis.name, analysis.trait_name,
    )?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            false,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, false, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, false, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, false, 1)?;
    }
    writeln!(w, "}}")?;

    Ok(())
}

fn need_generate_inherent(analysis: &analysis::object::Info) -> bool {
    analysis.has_constructors || analysis.has_functions || !need_generate_trait(analysis)
}

fn need_generate_trait(analysis: &analysis::object::Info) -> bool {
    analysis.generate_trait
}

pub fn generate_reexports(
    env: &Env,
    analysis: &analysis::object::Info,
    module_name: &str,
    contents: &mut Vec<String>,
    traits: &mut Vec<String>,
) {
    let mut cfgs: Vec<String> = Vec::new();
    if let Some(cfg) = general::cfg_condition_string(&analysis.cfg_condition, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::version_condition_string(env, analysis.version, false, 0) {
        cfgs.push(cfg);
    }
    contents.push("".to_owned());
    contents.extend_from_slice(&cfgs);
    contents.push(format!("mod {};", module_name));
    contents.extend_from_slice(&cfgs);

    let none_type = if !analysis.final_type {
        format!(", NONE_{}", analysis.name.to_snake().to_uppercase())
    } else {
        String::new()
    };

    contents.push(format!(
        "pub use self::{}::{{{}{}}};",
        module_name, analysis.name, none_type
    ));
    if need_generate_trait(analysis) {
        contents.extend_from_slice(&cfgs);
        contents.push(format!(
            "pub use self::{}::{};",
            module_name, analysis.trait_name
        ));
        for cfg in &cfgs {
            traits.push(format!("\t{}", cfg));
        }
        traits.push(format!("\tpub use super::{};", analysis.trait_name));
    }

    if !analysis.builder_properties.is_empty() {
        contents.extend_from_slice(&cfgs);
        contents.push(format!(
            "pub use self::{}::{}Builder;",
            module_name, analysis.name
        ));
    }
}
<commit_msg>codegen/object: Only re-export traits from `traits`/`prelude` module<commit_after>use super::{child_properties, function, general, properties, signal, trait_impls};
use crate::{
    analysis::special_functions::Type,
    analysis::{
        self,
        rust_type::{rust_type, rust_type_full},
    },
    case::CaseExt,
    codegen::general::{version_condition_no_doc, version_condition_string},
    env::Env,
    library, nameutil,
    traits::IntoString,
};
use std::io::{Result, Write};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::object::Info,
    generate_display_trait: bool,
) -> Result<()> {
    general::start_comments(w, &env.config)?;
    general::uses(w, env, &analysis.imports, analysis.version)?;

    general::define_object_type(
        w,
        env,
        &analysis.name,
        &analysis.c_type,
        analysis.c_class_type.as_deref(),
        &analysis.get_type,
        analysis.is_interface,
        &analysis.supertypes,
    )?;

    if need_generate_inherent(analysis) {
        writeln!(w)?;
        write!(w, "impl {} {{", analysis.name)?;
        for func_analysis in &analysis.constructors() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for func_analysis in &analysis.methods() {
                function::generate(
                    w,
                    env,
                    func_analysis,
                    Some(&analysis.specials),
                    analysis.version,
                    false,
                    false,
                    1,
                )?;
            }

            for property in &analysis.properties {
                properties::generate(w, env, property, false, false, 1)?;
            }

            for child_property in &analysis.child_properties {
                child_properties::generate(w, env, child_property, false, false, 1)?;
            }
        }

        for func_analysis in &analysis.functions() {
            function::generate(
                w,
                env,
                func_analysis,
                Some(&analysis.specials),
                analysis.version,
                false,
                false,
                1,
            )?;
        }

        if !need_generate_trait(analysis) {
            for signal_analysis in analysis
                .signals
                .iter()
                .chain(analysis.notify_signals.iter())
            {
                signal::generate(w, env, signal_analysis, false, false, 1)?;
            }
        }

        writeln!(w, "}}")?;

        general::declare_default_from_new(w, env, &analysis.name, &analysis.functions)?;
    }

    trait_impls::generate(
        w,
        env,
        &analysis.name,
        &analysis.functions,
        &analysis.specials,
        if need_generate_trait(analysis) {
            Some(&analysis.trait_name)
        } else {
            None
        },
        analysis.version,
    )?;

    if !analysis.builder_properties.is_empty() {
        writeln!(w)?;
        generate_builder(w, env, analysis)?;
    }

    if analysis.concurrency != library::Concurrency::None {
        writeln!(w)?;
    }

    match analysis.concurrency {
        library::Concurrency::Send | library::Concurrency::SendSync => {
            writeln!(w, "unsafe impl Send for {} {{}}", analysis.name)?;
        }
        library::Concurrency::SendUnique => {
            if env.namespaces.is_glib_crate {
                writeln!(w, "unsafe impl ::SendUnique for {} {{", analysis.name)?;
            } else {
                writeln!(w, "unsafe impl glib::SendUnique for {} {{", analysis.name)?;
            }

            writeln!(w, "    fn is_unique(&self) -> bool {{")?;
            writeln!(w, "        self.ref_count() == 1")?;
            writeln!(w, "    }}")?;

            writeln!(w, "}}")?;
        }
        _ => (),
    }

    if let library::Concurrency::SendSync = analysis.concurrency {
        writeln!(w, "unsafe impl Sync for {} {{}}", analysis.name)?;
    }

    if !analysis.final_type {
        writeln!(w)?;
        writeln!(
            w,
            "pub const NONE_{}: Option<&{}> = None;",
            analysis.name.to_snake().to_uppercase(),
            analysis.name
        )?;
    }

    if need_generate_trait(analysis) {
        writeln!(w)?;
        generate_trait(w, env, analysis)?;
    }

    if generate_display_trait && !analysis.specials.has_trait(Type::Display) {
        writeln!(w, "\nimpl fmt::Display for {} {{", analysis.name,)?;
        // Generate Display trait implementation.
        writeln!(
            w,
            "\tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
             \t\tf.write_str(\"{}\")\n\
             \t}}\n\
             }}",
            analysis.name
        )?;
    }

    Ok(())
}

// TODO: instead create a Vec<> inside the Builder instead of Options.
fn generate_builder(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    let mut methods = vec![];
    let mut properties = vec![];
    writeln!(w, "#[derive(Clone, Default)]")?;
    writeln!(w, "pub struct {}Builder {{", analysis.name)?;
    for property in &analysis.builder_properties {
        match rust_type(env, property.typ) {
            Ok(type_string) => {
                let type_string = match type_string.as_str() {
                    s if nameutil::is_gstring(s) => "String",
                    "Vec<GString>" | "Vec<glib::GString>" | "Vec<crate::GString>" => "Vec<String>",
                    typ => typ,
                };
                let mut param_type = rust_type_full(
                    env,
                    property.typ,
                    library::Nullable(false),
                    property.set_in_ref_mode,
                    library::ParameterScope::None,
                    library::Concurrency::None,
                )
                .into_string();
                let (param_type_override, bounds, conversion) = match &param_type[..] {
                    "&str" => (None, String::new(), ".to_string()"),
                    "&[&str]" => (Some("Vec<String>".to_string()), String::new(), ""),
                    _ if !property.bounds.is_empty() => {
                        let (bounds, _) = function::bounds(&property.bounds, &[], false, false);
                        let alias = property
                            .bounds
                            .get_parameter_alias_info(&property.name)
                            .map(|(alias, _)| format!("&{}", alias));
                        (alias, bounds, ".clone().upcast()")
                    }
                    typ if typ.starts_with('&') => (None, String::new(), ".clone()"),
                    _ => (None, String::new(), ""),
                };
                if let Some(param_type_override) = param_type_override {
                    param_type = param_type_override.to_string();
                }
                let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
                let version_condition_string =
                    version_condition_string(env, property.version, false, 1);
                if let Some(ref version_condition_string) = version_condition_string {
                    writeln!(w, "{}", version_condition_string)?;
                }
                writeln!(w, "    {}: Option<{}>,", name, type_string)?;
                let prefix = version_condition_string
                    .map(|version| format!("{}\n", version))
                    .unwrap_or_default();
                methods.push(format!(
                    "\n{prefix}    pub fn {name}{bounds}(mut self, {name}: {param_type}) -> Self {{
        self.{name} = Some({name}{conversion});
        self
    }}",
                    prefix = prefix,
                    param_type = param_type,
                    name = name,
                    conversion = conversion,
                    bounds = bounds
                ));
                properties.push(property);
            }
            Err(_) => writeln!(w, "    //{}: /*Unknown type*/,", property.name)?,
        }
    }
    writeln!(
        w,
        "}}

impl {}Builder {{
    pub fn new() -> Self {{
        Self::default()
    }}
",
        analysis.name
    )?;

    writeln!(
        w,
        "
    pub fn build(self) -> {} {{
        let mut properties: Vec<(&str, &dyn ToValue)> = vec![];",
        analysis.name
    )?;
    for property in &properties {
        let name = nameutil::mangle_keywords(nameutil::signal_to_snake(&property.name));
        version_condition_no_doc(w, env, property.version, false, 2)?;
        writeln!(
            w,
            "\
            if let Some(ref {field}) = self.{field} {{
                properties.push((\"{name}\", {field}));
            }}",
            name = property.name,
            field = name
        )?;
    }
    let glib_crate_name = if env.namespaces.is_glib_crate {
        "crate"
    } else {
        "glib"
    };
    writeln!(
        w,
        r#"        let ret = {}::Object::new::<{}>(&properties)
            .expect("object new");"#,
        glib_crate_name, analysis.name
    )?;
    if let Some(code) = analysis.builder_postprocess.as_ref() {
        writeln!(w, "        {{\n            {}\n        }}", code)?;
    }
    writeln!(w, "    ret\n    }}")?;
    for method in methods {
        writeln!(w, "{}", method)?;
    }
    writeln!(w, "}}")
}

fn generate_trait(w: &mut dyn Write, env: &Env, analysis: &analysis::object::Info) -> Result<()> {
    write!(w, "pub trait {}: 'static {{", analysis.trait_name)?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            true,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, true, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, true, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, true, 1)?;
    }
    writeln!(w, "}}")?;

    writeln!(w)?;
    write!(
        w,
        "impl<O: IsA<{}>> {} for O {{",
        analysis.name, analysis.trait_name,
    )?;

    for func_analysis in &analysis.methods() {
        function::generate(
            w,
            env,
            func_analysis,
            Some(&analysis.specials),
            analysis.version,
            true,
            false,
            1,
        )?;
    }
    for property in &analysis.properties {
        properties::generate(w, env, property, true, false, 1)?;
    }
    for child_property in &analysis.child_properties {
        child_properties::generate(w, env, child_property, true, false, 1)?;
    }
    for signal_analysis in analysis
        .signals
        .iter()
        .chain(analysis.notify_signals.iter())
    {
        signal::generate(w, env, signal_analysis, true, false, 1)?;
    }
    writeln!(w, "}}")?;

    Ok(())
}

fn need_generate_inherent(analysis: &analysis::object::Info) -> bool {
    analysis.has_constructors || analysis.has_functions || !need_generate_trait(analysis)
}

fn need_generate_trait(analysis: &analysis::object::Info) -> bool {
    analysis.generate_trait
}

pub fn generate_reexports(
    env: &Env,
    analysis: &analysis::object::Info,
    module_name: &str,
    contents: &mut Vec<String>,
    traits: &mut Vec<String>,
) {
    let mut cfgs: Vec<String> = Vec::new();
    if let Some(cfg) = general::cfg_condition_string(&analysis.cfg_condition, false, 0) {
        cfgs.push(cfg);
    }
    if let Some(cfg) = general::version_condition_string(env, analysis.version, false, 0) {
        cfgs.push(cfg);
    }
    contents.push("".to_owned());
    contents.extend_from_slice(&cfgs);
    contents.push(format!("mod {};", module_name));
    contents.extend_from_slice(&cfgs);

    let none_type = if !analysis.final_type {
        format!(", NONE_{}", analysis.name.to_snake().to_uppercase())
    } else {
        String::new()
    };

    contents.push(format!(
        "pub use self::{}::{{{}{}}};",
        module_name, analysis.name, none_type
    ));
    if need_generate_trait(analysis) {
        for cfg in &cfgs {
            traits.push(format!("\t{}", cfg));
        }
        traits.push(format!(
            "\tpub use super::{}::{};",
            module_name, analysis.trait_name
        ));
    }

    if !analysis.builder_properties.is_empty() {
        contents.extend_from_slice(&cfgs);
        contents.push(format!(
            "pub use self::{}::{}Builder;",
            module_name, analysis.name
        ));
    }
}
<|endoftext|>


<commit_before>//! Checks if an AST is valid

use super::ast::*;
use super::visit::*;
use super::span::*;

use term::Terminal;

use std::fmt;
use std::io::{self, Write};

#[derive(Clone, Debug)]
pub struct CheckError {
    pub msg: &'static str,
    pub detail: Option<String>,
    pub span: Span,
}

impl CheckError {
    /// Prints this `CheckError` to a terminal.
    pub fn format<W: Write>(&self, code: &str, source_name: &str, t: &mut Terminal<Output=W>)
    -> io::Result<()> {
        let mut msg = self.msg.to_string();
        if let Some(ref detail) = self.detail {
            msg.push_str(detail.as_ref());
        }
        try!(self.span.print_with_err(code, source_name, msg.as_ref(), t));

        Ok(())
    }
}

impl fmt::Display for CheckError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.detail {
            None => {
                fmt.write_fmt(format_args!("{}", self.msg))
            },
            Some(ref detail) => {
                fmt.write_fmt(format_args!("{}: {}", self.msg, detail))
            }
        }
    }
}

struct Checker {
    errs: Vec<CheckError>,
    vararg_func: bool,
    looplvl: u32,
}

impl <'a> Visitor<'a> for Checker {
    fn visit_stmt(&mut self, s: &Stmt) {
        match s.value {
            SBreak => {
                if self.looplvl == 0 {
                    self.errs.push(CheckError {
                        msg: "Use of `break` outside of loop",
                        detail: None,
                        span: s.span,
                    });
                }
            },
            SFor{..} | SWhile{..} | SRepeat{..} => {
                self.looplvl += 1;
                walk_stmt_ref(s, self);
                self.looplvl -= 1;
            },
            _ => walk_stmt_ref(s, self),
        }
    }

    fn visit_expr(&mut self, e: &Expr) {
        match e.value {
            EVarArgs => {
                if !self.vararg_func {
                    self.errs.push(CheckError {
                        msg: "Use of ... outside varargs function",
                        detail: None,
                        span: e.span,
                    });
                }
            },
            _ => {},
        }
    }

    fn visit_func(&mut self, func: &Function) {
        let res = check_func(func);
        if let Err(mut new_errs) = res {
            self.errs.append(&mut new_errs);
        }
    }
}

/// Checks that the passed function contains only valid statements.
///
/// Returns a non-empty `Vec<CheckError>` if any errors were found.
pub fn check_func(func: &Function) -> Result<(), Vec<CheckError>> {
    let mut ch = Checker {
        errs: vec![],
        vararg_func: func.varargs,
        looplvl: 0,
    };

    ch.visit_block(&func.body);

    if ch.errs.len() == 0 {
        Ok(())
    } else {
        Err(ch.errs)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::parse_main;

    #[test]
    fn test() {
        assert!(check_func(&parse_main("break").unwrap()).is_err());
        assert!(check_func(&parse_main("for i=0,1 do break end").unwrap()).is_ok());
        assert!(check_func(&parse_main("i = ...").unwrap()).is_ok());
        assert!(check_func(&parse_main(r#"
function f(a)
    a = ...
end
"#).unwrap()).is_err());
    }
}
<commit_msg>lowercase error message<commit_after>//! Checks if an AST is valid

use super::ast::*;
use super::visit::*;
use super::span::*;

use term::Terminal;

use std::fmt;
use std::io::{self, Write};

#[derive(Clone, Debug)]
pub struct CheckError {
    pub msg: &'static str,
    pub detail: Option<String>,
    pub span: Span,
}

impl CheckError {
    /// Prints this `CheckError` to a terminal.
    pub fn format<W: Write>(&self, code: &str, source_name: &str, t: &mut Terminal<Output=W>)
    -> io::Result<()> {
        let mut msg = self.msg.to_string();
        if let Some(ref detail) = self.detail {
            msg.push_str(detail.as_ref());
        }
        try!(self.span.print_with_err(code, source_name, msg.as_ref(), t));

        Ok(())
    }
}

impl fmt::Display for CheckError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self.detail {
            None => {
                fmt.write_fmt(format_args!("{}", self.msg))
            },
            Some(ref detail) => {
                fmt.write_fmt(format_args!("{}: {}", self.msg, detail))
            }
        }
    }
}

struct Checker {
    errs: Vec<CheckError>,
    vararg_func: bool,
    looplvl: u32,
}

impl <'a> Visitor<'a> for Checker {
    fn visit_stmt(&mut self, s: &Stmt) {
        match s.value {
            SBreak => {
                if self.looplvl == 0 {
                    self.errs.push(CheckError {
                        msg: "use of `break` outside of loop",
                        detail: None,
                        span: s.span,
                    });
                }
            },
            SFor{..} | SWhile{..} | SRepeat{..} => {
                self.looplvl += 1;
                walk_stmt_ref(s, self);
                self.looplvl -= 1;
            },
            _ => walk_stmt_ref(s, self),
        }
    }

    fn visit_expr(&mut self, e: &Expr) {
        match e.value {
            EVarArgs => {
                if !self.vararg_func {
                    self.errs.push(CheckError {
                        msg: "use of ... outside varargs function",
                        detail: None,
                        span: e.span,
                    });
                }
            },
            _ => {},
        }
    }

    fn visit_func(&mut self, func: &Function) {
        let res = check_func(func);
        if let Err(mut new_errs) = res {
            self.errs.append(&mut new_errs);
        }
    }
}

/// Checks that the passed function contains only valid statements.
///
/// Returns a non-empty `Vec<CheckError>` if any errors were found.
pub fn check_func(func: &Function) -> Result<(), Vec<CheckError>> {
    let mut ch = Checker {
        errs: vec![],
        vararg_func: func.varargs,
        looplvl: 0,
    };

    ch.visit_block(&func.body);

    if ch.errs.len() == 0 {
        Ok(())
    } else {
        Err(ch.errs)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parser::parse_main;

    #[test]
    fn test() {
        assert!(check_func(&parse_main("break").unwrap()).is_err());
        assert!(check_func(&parse_main("for i=0,1 do break end").unwrap()).is_ok());
        assert!(check_func(&parse_main("i = ...").unwrap()).is_ok());
        assert!(check_func(&parse_main(r#"
function f(a)
    a = ...
end
"#).unwrap()).is_err());
    }
}
<|endoftext|>


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

// Coroutines represent nothing more than a context and a stack
// segment.

use context::Context;
use stack::{StackPool, Stack};

/// A coroutine is nothing more than a (register context, stack) pair.
#[derive(Debug)]
pub struct Coroutine {
    /// The segment of stack on which the task is currently running or
    /// if the task is blocked, on which the task will resume
    /// execution.
    current_stack_segment: Stack,

    /// Always valid if the task is alive and not running.
    saved_context: Context,
}

impl Coroutine {
    pub fn empty() -> Coroutine {
        Coroutine {
            current_stack_segment: unsafe { Stack::dummy_stack() },
            saved_context: Context::empty(),
        }
    }

    /// Destroy coroutine and try to reuse std::stack segment.
    pub fn recycle(self, stack_pool: &mut StackPool) {
        let Coroutine { current_stack_segment, .. } = self;
        stack_pool.give_stack(current_stack_segment);
    }
}
<commit_msg>just derive Debug trait<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Coroutines represent nothing more than a context and a stack
// segment.

use context::Context;
use stack::{StackPool, Stack};

/// A coroutine is nothing more than a (register context, stack) pair.
#[derive(Debug)]
pub struct Coroutine {
    /// The segment of stack on which the task is currently running or
    /// if the task is blocked, on which the task will resume
    /// execution.
    current_stack_segment: Stack,

    /// Always valid if the task is alive and not running.
    saved_context: Context,
}

impl Coroutine {
    pub fn empty() -> Coroutine {
        Coroutine {
            current_stack_segment: unsafe { Stack::dummy_stack() },
            saved_context: Context::empty(),
        }
    }

    /// Destroy coroutine and try to reuse std::stack segment.
    pub fn recycle(self, stack_pool: &mut StackPool) {
        let Coroutine { current_stack_segment, .. } = self;
        stack_pool.give_stack(current_stack_segment);
    }
}

#[cfg(test)]
mod test {

    #[test]
    fn test_coroutine_basic() {

        extern fn init_fn(arg: usize, f: *mut ()) -> ! {
            let func: Box<Thunk> = unsafe { transmute(f) };
            if let Err(cause) = unsafe { try(move|| func.invoke(())) } {
                error!("Panicked inside: {:?}", cause.downcast::<&str>());
            }

            let ctx: &Context = unsafe { transmute(arg) };

            let mut dummy = Context::empty();
            Context::swap(&mut dummy, ctx);

            unreachable!();
        }

        let mut pool = StackPool::new();

        let mut coro = Coroutine::empty();
        coro.current_stack_segment = pool.take_stack();
    }
}
<|endoftext|>


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

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(): ino {}", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        attr.ino = 1;
        attr.kind = FileType::Directory;
        attr.perm = USER_DIR;
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            reply.attr(&ttl, &attr);
        } else {
            reply.error(ENOSYS);
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(): ino {}, fh {}, ofset {}", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<commit_msg>Trying to expand getattr.<commit_after>extern crate fuse;
extern crate time;
extern crate libc;
extern crate serialize;

use std::io::{FileType, USER_FILE, USER_DIR};
use std::mem;
use std::os;
use libc::{ENOENT, ENOSYS};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(): ino {}", ino);
        let mut attr: FileAttr = unsafe { mem::zeroed() };
        let ttl = Timespec::new(1, 0);
        if ino == 1 {
            attr.ino = 1;
            attr.kind = FileType::Directory;
            attr.perm = USER_DIR;
            reply.attr(&ttl, &attr);
        } else {
            let tree_index = (ino - 2) as uint;
            println!("\ttree_index {}", tree_index);
            if tree_index < self.tree.len() {
                attr.ino = ino;
                attr.kind = FileType::RegularFile;
                attr.perm = USER_FILE;
                reply.attr(&ttl, &attr);
            } else {
                reply.error(ENOSYS);
            }
        }
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(): ino {}, fh {}, ofset {}", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (i, key) in self.tree.keys().enumerate() {
                    let inode: u64 = 2 + i as u64;
                    let offset: u64 = 2 + i as u64;
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day N)");
    let data = json::from_str("{\"foo\": \"bar\", \"answer\": 42}").unwrap();
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem { tree: tree.clone() };
    let mountpoint = Path::new(os::args()[1].as_slice());
    fuse::mount(fs, &mountpoint, &[]);
}
<|endoftext|>


<commit_before>use std::io::{Error, Read, ErrorKind};
use std::mem::transmute;
use std::cmp::min;
use std::ptr::copy_nonoverlapping;

use sodiumoxide::crypto::secretbox;
use sodiumoxide::utils::memzero;

use crypto::{CYPHER_HEADER_SIZE, CYPHER_HEADER_SIZE_U16, MAX_PACKET_SIZE, MAX_PACKET_USIZE,
             PlainHeader, decrypt_header_inplace, decrypt_packet_inplace};

const BUFFER_SIZE: usize = CYPHER_HEADER_SIZE + MAX_PACKET_USIZE;

/// The error value signaling that a header is not correctly authenticated.
pub const UNAUTHENTICATED_HEADER: &'static str = "header_auth";

/// The error value signaling that a header claims an invalid packet length.
pub const INVALID_LENGTH: &'static str = "invalid_length";

/// The error value used signaling that a packet is not correctly authenticated.
pub const UNAUTHENTICATED_PACKET: &'static str = "packet_auth";

/// The error value signaling that the box stream reached an unauthenticated eof.
pub const UNAUTHENTICATED_EOF: &'static str = "unauthenticated_eof";

// Implements the base functionality for creating decrypting wrappers around `io::Read`s.
pub struct Decryptor {
    // Bytes are read into this buffer and get decrypted in-place
    buffer: [u8; BUFFER_SIZE],
    state: State,
}

impl Decryptor {
    pub fn new() -> Decryptor {
        Decryptor {
            buffer: [0; BUFFER_SIZE],
            state: ReadCypherHeader { offset: 0 },
        }
    }

    // A Read wrapper using the decryptor should delegate to this method in its `read` implementation.
    //
    // If this returns Ok(0) and the provided buffer was not 0 bytes in length, a final header was read
    // and no more data will be emitted. If the underlying Read emitted 0 bytes although it was not
    // given a 0 length buffer, this results in an io::Error of kind `UnexpectedEof` (since EOF
    // must be signaled by the final header).
    pub fn read<R: Read>(&mut self,
                         buf: &mut [u8],
                         reader: &mut R,
                         key: &secretbox::Key,
                         nonce: &mut secretbox::Nonce)
                         -> Result<usize, Error> {
        match self.state {
            ReadCypherHeader { offset } => {
                debug_assert!(offset < CYPHER_HEADER_SIZE_U16);

                let new_offset = offset +
                                 (read_nonzero(reader,
                                               &mut self.buffer[(offset as usize)..
                                                    CYPHER_HEADER_SIZE])? as
                                  u16);

                if new_offset < CYPHER_HEADER_SIZE_U16 {
                    self.state = ReadCypherHeader { offset: new_offset };
                    return self.read(buf, reader, key, nonce);
                } else {
                    let is_header_valid =
                        unsafe {
                            decrypt_header_inplace(&mut *(self.buffer.as_mut_ptr() as
                                                          *mut [u8; CYPHER_HEADER_SIZE]),
                                                   &key.0,
                                                   &mut nonce.0)
                        };

                    if is_header_valid {
                        let plain_header = unsafe { self.plain_header() };

                        if plain_header.is_final_header() {
                            return Ok(0);
                        } else {
                            if plain_header.get_packet_len() > MAX_PACKET_SIZE {
                                return Err(Error::new(ErrorKind::InvalidData, INVALID_LENGTH));
                            } else {
                                self.state = ReadCypherPacket {
                                    offset: 0,
                                    length: plain_header.get_packet_len(),
                                };
                                return self.read(buf, reader, key, nonce);
                            }
                        }
                    } else {
                        return Err(Error::new(ErrorKind::InvalidData, UNAUTHENTICATED_HEADER));
                    }
                }
            }

            ReadCypherPacket { offset, length } => {
                debug_assert!(offset < length);
                debug_assert!(length <= MAX_PACKET_SIZE);

                let new_offset = offset +
                                 (read_nonzero(reader,
                                               &mut self.buffer[CYPHER_HEADER_SIZE + (offset as usize)..
                                                    CYPHER_HEADER_SIZE +
                                                    (length as usize)])? as
                                  u16);

                if new_offset < length {
                    self.state = ReadCypherPacket {
                        offset: new_offset,
                        length: length,
                    };
                    return self.read(buf, reader, key, nonce);
                } else {
                    let plain_header = unsafe { self.plain_header() };

                    let is_packet_valid = unsafe {
                        decrypt_packet_inplace(self.buffer
                                                   .as_mut_ptr()
                                                   .offset(CYPHER_HEADER_SIZE as isize),
                                               &plain_header,
                                               &key.0,
                                               &mut nonce.0)
                    };

                    if is_packet_valid {
                        self.state = Readable {
                            offset: 0,
                            length: length,
                        };
                        return self.read(buf, reader, key, nonce);
                    } else {
                        return Err(Error::new(ErrorKind::InvalidData, UNAUTHENTICATED_PACKET));
                    }
                }
            }

            Readable { offset, length } => {
                debug_assert!(offset < length);
                debug_assert!(length <= MAX_PACKET_SIZE);

                let read = min(buf.len(), (length - offset) as usize);

                unsafe {
                    copy_nonoverlapping(self.buffer
                                            .as_ptr()
                                            .offset(CYPHER_HEADER_SIZE as isize +
                                                    offset as isize),
                                        buf.as_mut_ptr(),
                                        read);
                }

                if offset + (read as u16) < length {
                    self.state = Readable {
                        offset: offset + (read as u16),
                        length: length,
                    };
                } else {
                    self.state = ReadCypherHeader { offset: 0 }
                }

                return Ok(read);
            }
        }
    }

    // This unsafely casts the first 2 + secretbox::MACBYTES bytes of the buffer as a PlainHeader.
    // Everything goes horribly wrong if these bytes don't actually contain a decrypted header.
    unsafe fn plain_header(&self) -> PlainHeader {
        transmute::<[u8; secretbox::MACBYTES + 2], PlainHeader>(*(self.buffer.as_ptr() as
                                                                  *const [u8;
                                                                          secretbox::MACBYTES + 2]))
    }
}

/// Zero buffered data on dropping.
impl Drop for Decryptor {
    fn drop(&mut self) {
        memzero(&mut self.buffer);
    }
}

// State of the decryptor state machine. When `read` is called on the Decryptor, it advances
// through the state machine, providing as many bytes as possible until either the given buffer is
// full, or the underlying AsyncRead blocks.
//
// Initial state is ReadCypherHeader{offset: 0}.
enum State {
    // Read (CYPHER_HEADER_SIZE - offset) bytes from the given Readable to buffer[offset], then
    // offset += read_bytes.
    // Once offset == CYPHER_HEADER_SIZE, the CypherHeader is verified, decrypted in place, and the
    //  state machine advances to ReadCypherPacket {offset: 0, length: length_from_header}.
    //
    // Invariants: offset < CYPHER_HEADER_SIZE (since the state changes once it reaches CYPHER_HEADER_SIZE)
    ReadCypherHeader { offset: u16 },
    // Read (length - offset) bytes from the given Readable to buffer[CYPHER_HEADER_SIZE + offset],
    // then offset += read_bytes.
    // Once offset == length, the CypherPacket is verified, decrypted in place, and the state
    // machine advances to Readable {offset: 0, length: length}.
    //
    // Invariants: offset < length, length <= MAX_PACKET_SIZE
    ReadCypherPacket { offset: u16, length: u16 },
    // `read` should read (length - offset) bytes from buffer[CYPHER_HEADER_SIZE + offset], then
    // offset += written_bytes.
    // Once offset == length, the state machine advances to WriteCypherHeader { offset: 0}.
    //
    // Invariants: offset < length, length <= MAX_PACKET_SIZE
    Readable { offset: u16, length: u16 },
}
use decryptor::State::*;

// Helper function which delegates to `Reader::read`, but returns an Error of kind UnexpectedEof
// if zero bytes were read although `buf` had length greater than 0.
fn read_nonzero<R: Read>(r: &mut R, buf: &mut [u8]) -> Result<usize, Error> {
    let read = r.read(buf)?;
    if read == 0 && buf.len() > 0 {
        return Err(Error::new(ErrorKind::UnexpectedEof, UNAUTHENTICATED_EOF));
    } else {
        return Ok(read);
    }
}
<commit_msg>Make error messages human-readable<commit_after>use std::io::{Error, Read, ErrorKind};
use std::mem::transmute;
use std::cmp::min;
use std::ptr::copy_nonoverlapping;

use sodiumoxide::crypto::secretbox;
use sodiumoxide::utils::memzero;

use crypto::{CYPHER_HEADER_SIZE, CYPHER_HEADER_SIZE_U16, MAX_PACKET_SIZE, MAX_PACKET_USIZE,
             PlainHeader, decrypt_header_inplace, decrypt_packet_inplace};

const BUFFER_SIZE: usize = CYPHER_HEADER_SIZE + MAX_PACKET_USIZE;

/// The error value signaling that a header is not correctly authenticated.
pub const UNAUTHENTICATED_HEADER: &'static str = "read unauthenticated header";

/// The error value signaling that a header claims an invalid packet length.
pub const INVALID_LENGTH: &'static str = "read header containing invalid length";

/// The error value used signaling that a packet is not correctly authenticated.
pub const UNAUTHENTICATED_PACKET: &'static str = "read unauthenticated packet";

/// The error value signaling that the box stream reached an unauthenticated eof.
pub const UNAUTHENTICATED_EOF: &'static str = "reached unauthenticated eof";

// Implements the base functionality for creating decrypting wrappers around `io::Read`s.
pub struct Decryptor {
    // Bytes are read into this buffer and get decrypted in-place
    buffer: [u8; BUFFER_SIZE],
    state: State,
}

impl Decryptor {
    pub fn new() -> Decryptor {
        Decryptor {
            buffer: [0; BUFFER_SIZE],
            state: ReadCypherHeader { offset: 0 },
        }
    }

    // A Read wrapper using the decryptor should delegate to this method in its `read` implementation.
    //
    // If this returns Ok(0) and the provided buffer was not 0 bytes in length, a final header was read
    // and no more data will be emitted. If the underlying Read emitted 0 bytes although it was not
    // given a 0 length buffer, this results in an io::Error of kind `UnexpectedEof` (since EOF
    // must be signaled by the final header).
    pub fn read<R: Read>(&mut self,
                         buf: &mut [u8],
                         reader: &mut R,
                         key: &secretbox::Key,
                         nonce: &mut secretbox::Nonce)
                         -> Result<usize, Error> {
        match self.state {
            ReadCypherHeader { offset } => {
                debug_assert!(offset < CYPHER_HEADER_SIZE_U16);

                let new_offset = offset +
                                 (read_nonzero(reader,
                                               &mut self.buffer[(offset as usize)..
                                                    CYPHER_HEADER_SIZE])? as
                                  u16);

                if new_offset < CYPHER_HEADER_SIZE_U16 {
                    self.state = ReadCypherHeader { offset: new_offset };
                    return self.read(buf, reader, key, nonce);
                } else {
                    let is_header_valid =
                        unsafe {
                            decrypt_header_inplace(&mut *(self.buffer.as_mut_ptr() as
                                                          *mut [u8; CYPHER_HEADER_SIZE]),
                                                   &key.0,
                                                   &mut nonce.0)
                        };

                    if is_header_valid {
                        let plain_header = unsafe { self.plain_header() };

                        if plain_header.is_final_header() {
                            return Ok(0);
                        } else {
                            if plain_header.get_packet_len() > MAX_PACKET_SIZE {
                                return Err(Error::new(ErrorKind::InvalidData, INVALID_LENGTH));
                            } else {
                                self.state = ReadCypherPacket {
                                    offset: 0,
                                    length: plain_header.get_packet_len(),
                                };
                                return self.read(buf, reader, key, nonce);
                            }
                        }
                    } else {
                        return Err(Error::new(ErrorKind::InvalidData, UNAUTHENTICATED_HEADER));
                    }
                }
            }

            ReadCypherPacket { offset, length } => {
                debug_assert!(offset < length);
                debug_assert!(length <= MAX_PACKET_SIZE);

                let new_offset = offset +
                                 (read_nonzero(reader,
                                               &mut self.buffer[CYPHER_HEADER_SIZE + (offset as usize)..
                                                    CYPHER_HEADER_SIZE +
                                                    (length as usize)])? as
                                  u16);

                if new_offset < length {
                    self.state = ReadCypherPacket {
                        offset: new_offset,
                        length: length,
                    };
                    return self.read(buf, reader, key, nonce);
                } else {
                    let plain_header = unsafe { self.plain_header() };

                    let is_packet_valid = unsafe {
                        decrypt_packet_inplace(self.buffer
                                                   .as_mut_ptr()
                                                   .offset(CYPHER_HEADER_SIZE as isize),
                                               &plain_header,
                                               &key.0,
                                               &mut nonce.0)
                    };

                    if is_packet_valid {
                        self.state = Readable {
                            offset: 0,
                            length: length,
                        };
                        return self.read(buf, reader, key, nonce);
                    } else {
                        return Err(Error::new(ErrorKind::InvalidData, UNAUTHENTICATED_PACKET));
                    }
                }
            }

            Readable { offset, length } => {
                debug_assert!(offset < length);
                debug_assert!(length <= MAX_PACKET_SIZE);

                let read = min(buf.len(), (length - offset) as usize);

                unsafe {
                    copy_nonoverlapping(self.buffer
                                            .as_ptr()
                                            .offset(CYPHER_HEADER_SIZE as isize +
                                                    offset as isize),
                                        buf.as_mut_ptr(),
                                        read);
                }

                if offset + (read as u16) < length {
                    self.state = Readable {
                        offset: offset + (read as u16),
                        length: length,
                    };
                } else {
                    self.state = ReadCypherHeader { offset: 0 }
                }

                return Ok(read);
            }
        }
    }

    // This unsafely casts the first 2 + secretbox::MACBYTES bytes of the buffer as a PlainHeader.
    // Everything goes horribly wrong if these bytes don't actually contain a decrypted header.
    unsafe fn plain_header(&self) -> PlainHeader {
        transmute::<[u8; secretbox::MACBYTES + 2], PlainHeader>(*(self.buffer.as_ptr() as
                                                                  *const [u8;
                                                                          secretbox::MACBYTES + 2]))
    }
}

/// Zero buffered data on dropping.
impl Drop for Decryptor {
    fn drop(&mut self) {
        memzero(&mut self.buffer);
    }
}

// State of the decryptor state machine. When `read` is called on the Decryptor, it advances
// through the state machine, providing as many bytes as possible until either the given buffer is
// full, or the underlying AsyncRead blocks.
//
// Initial state is ReadCypherHeader{offset: 0}.
enum State {
    // Read (CYPHER_HEADER_SIZE - offset) bytes from the given Readable to buffer[offset], then
    // offset += read_bytes.
    // Once offset == CYPHER_HEADER_SIZE, the CypherHeader is verified, decrypted in place, and the
    //  state machine advances to ReadCypherPacket {offset: 0, length: length_from_header}.
    //
    // Invariants: offset < CYPHER_HEADER_SIZE (since the state changes once it reaches CYPHER_HEADER_SIZE)
    ReadCypherHeader { offset: u16 },
    // Read (length - offset) bytes from the given Readable to buffer[CYPHER_HEADER_SIZE + offset],
    // then offset += read_bytes.
    // Once offset == length, the CypherPacket is verified, decrypted in place, and the state
    // machine advances to Readable {offset: 0, length: length}.
    //
    // Invariants: offset < length, length <= MAX_PACKET_SIZE
    ReadCypherPacket { offset: u16, length: u16 },
    // `read` should read (length - offset) bytes from buffer[CYPHER_HEADER_SIZE + offset], then
    // offset += written_bytes.
    // Once offset == length, the state machine advances to WriteCypherHeader { offset: 0}.
    //
    // Invariants: offset < length, length <= MAX_PACKET_SIZE
    Readable { offset: u16, length: u16 },
}
use decryptor::State::*;

// Helper function which delegates to `Reader::read`, but returns an Error of kind UnexpectedEof
// if zero bytes were read although `buf` had length greater than 0.
fn read_nonzero<R: Read>(r: &mut R, buf: &mut [u8]) -> Result<usize, Error> {
    let read = r.read(buf)?;
    if read == 0 && buf.len() > 0 {
        return Err(Error::new(ErrorKind::UnexpectedEof, UNAUTHENTICATED_EOF));
    } else {
        return Ok(read);
    }
}
<|endoftext|>


<commit_before>use artano::Annotation;
use config::resource::Resource;
use error::Cause;
use std::borrow::Cow;
use std::error;
use std::fmt;
use std::path::{Path, PathBuf};
use std::result;

// How the hell do you make a path buffer from command line input if command line input is a
// string but a path buffer itself is technically not because it isn't validated UTF8?

type Result<T> = result::Result<T, BuildOptionsError>;

#[derive(Debug)]
pub struct Options {
    pub base_image: Resource,
    pub annotations: Vec<Annotation>,
    pub output_path: PathBuf,
    pub output_format: OutputFormat,
    pub scale_mult: f32,
    pub font_path: PathBuf,
    pub debug: bool,
}

#[derive(Copy, Clone, Debug)]
pub enum OutputFormat {
    Jpg,
    Png,
}

impl Options {
    pub fn from_args() -> Result<Self> {
        read_command()
    }
}

pub struct OptionsBuilder {
    base_image: Option<String>,
    annotations: Vec<Annotation>,
    output_path: Option<String>,
    output_format: OutputFormat,
    scale_mult: f32,
    font_path: Cow<'static, str>,
    debug: bool,
}

impl OptionsBuilder {
    fn new() -> OptionsBuilder {
        OptionsBuilder {
            base_image: None,
            annotations: Vec::new(),
            output_path: None,
            output_format: OutputFormat::Jpg,
            scale_mult: 1.0,
            font_path: default_font(),
            debug: false,
        }
    }
}

impl OptionsBuilder {
    fn build(self) -> Result<Options> {
        let input_path: PathBuf = self.base_image.as_ref().unwrap().into();
        if input_path.file_name().is_none() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::ImagePath,
                description: Cow::from(
                    "The provided image path does not appear to have a filename",
                ),
                cause: None,
            });
        }

        let output_format = self.output_format;
        let output_path = self.output_path.map(|s| s.into()).unwrap_or_else(|| {
            create_output_file_path(&input_path, output_format)
        });

        let annotations = if self.annotations.is_empty() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::Annotations,
                description: Cow::from("No annotations provided"),
                cause: None,
            });
        } else {
            self.annotations
        };

        Ok(Options {
            base_image: Resource::new(self.base_image.unwrap()),
            annotations,
            output_path,
            output_format,
            scale_mult: self.scale_mult,
            font_path: self.font_path.to_string().into(),
            debug: self.debug,
        })
    }
}

#[derive(Debug)]
pub struct BuildOptionsError {
    kind: BuildOptionsErrorKind,
    description: Cow<'static, str>,
    cause: Option<Cause>,
}

#[derive(Debug)]
enum BuildOptionsErrorKind {
    Annotations,
    ImagePath,
    ScalingMultiplier,
}

impl fmt::Display for BuildOptionsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.description)
    }
}

impl error::Error for BuildOptionsError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&error::Error> {
        self.cause.as_ref().map(|cause| cause.as_ref())
    }
}

fn read_command() -> Result<Options> {
    use clap::ArgGroup;

    let text_group = ArgGroup::with_name("text_group")
        .args(&["caption", "bottom", "top", "middle"])
        .multiple(true);

    let encoding_group = ArgGroup::with_name("enc_group").args(&["jpg", "png"]);

    let app =
        clap_app!(annatar =>
        (version: crate_version!())
        (author: crate_authors!())
        (about: crate_description!())
        (@arg image: +required "Path to an image to be annotated")
        (@arg caption: "A message to be added to the bottom of the image")
        (@arg bottom: -b --bottom +takes_value "A message to be added to the bottom of the image")
        (@arg top: -t --top +takes_value "A message to be added to the top of the image")
        (@arg middle: -m --middle +takes_value "A message to be added to the middle of the image")
        (@arg output: -o --output +takes_value
            "Sets an output path for the new image (default: <image path>/<image name>.ann.<ext>)")
        (@arg scale: -s --scale +takes_value "Sets the scale multiplier for annotations")
        (@arg font: -f --font +takes_value "Sets the path of the font to be used (default: Impact)")
        (@arg debug: -d --debug "Save edge detection ... thing to disk")
        (@arg jpg: --jpg "Set JPG mode (default)")
        (@arg png: --png "Set PNG mode")
    );

    // Much easier to set up argument groups outside macro.
    let app = app.group(text_group).group(encoding_group);

    let matches = app.get_matches();
    let mut options = OptionsBuilder::new();

    options.base_image = Some(matches.value_of("image").unwrap().to_string());
    options.output_path = matches.value_of("output").map(|s| s.to_string());

    if let Some(scale_multiplier) = matches.value_of("scale") {
        let multiplier = scale_multiplier.parse::<f32>().map_err(|e| {
            BuildOptionsError {
                kind: BuildOptionsErrorKind::ScalingMultiplier,
                description: Cow::from("Scaling multiplier must be a decimal value"),
                cause: Some(Box::new(e)),
            }
        })?;
        options.scale_mult = multiplier;
    }

    if let Some(font_path) = matches.value_of("font") {
        options.font_path = Cow::from(font_path.to_string());
    }

    if let Some(caption) = matches.value_of("caption") {
        options.annotations.push(Annotation::bottom(caption));
    }

    if let Some(caption) = matches.value_of("top") {
        options.annotations.push(Annotation::top(caption));
    }

    if let Some(caption) = matches.value_of("middle") {
        options.annotations.push(Annotation::middle(caption));
    }

    if let Some(caption) = matches.value_of("bottom") {
        options.annotations.push(Annotation::bottom(caption));
    }

    options.output_format = {
        if matches.is_present("jpg") {
            OutputFormat::Jpg
        } else {
            OutputFormat::Png
        }
    };

    options.debug = matches.is_present("debug");

    options.build()
}

#[cfg(target_os = "windows")]
fn default_font() -> Cow<'static, str> {
    Cow::from("C:/Windows/Fonts/Impact.ttf")
}

#[cfg(target_os = "macos")]
fn default_font() -> Cow<'static, str> {
    Cow::from("/Library/Fonts/Impact.ttf")
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn default_font() -> Cow<'static, str> {
    unimplemented!("Honestly, getting a font on Linux is going to be an adventure.");
}

fn create_output_file_path(input_path: &Path, output_format: OutputFormat) -> PathBuf {
    // I unwrap this because clap already converted it to a string, implying it's valid utf-8.
    let mut file_name = input_path
        .file_name()
        .unwrap()
        .to_str()
        .unwrap()
        .to_string();

    if let Some(last_segment_idx) = file_name.rfind('.') {
        file_name.truncate(last_segment_idx);
    }

    match output_format {
        OutputFormat::Png => file_name.push_str("-annotated.png"),
        _ => file_name.push_str("-annotated.jpg"),
    }

    file_name.into()
}
<commit_msg>Note that png is the default output type<commit_after>use artano::Annotation;
use config::resource::Resource;
use error::Cause;
use std::borrow::Cow;
use std::error;
use std::fmt;
use std::path::{Path, PathBuf};
use std::result;

// How the hell do you make a path buffer from command line input if command line input is a
// string but a path buffer itself is technically not because it isn't validated UTF8?

type Result<T> = result::Result<T, BuildOptionsError>;

#[derive(Debug)]
pub struct Options {
    pub base_image: Resource,
    pub annotations: Vec<Annotation>,
    pub output_path: PathBuf,
    pub output_format: OutputFormat,
    pub scale_mult: f32,
    pub font_path: PathBuf,
    pub debug: bool,
}

#[derive(Copy, Clone, Debug)]
pub enum OutputFormat {
    Jpg,
    Png,
}

impl Options {
    pub fn from_args() -> Result<Self> {
        read_command()
    }
}

pub struct OptionsBuilder {
    base_image: Option<String>,
    annotations: Vec<Annotation>,
    output_path: Option<String>,
    output_format: OutputFormat,
    scale_mult: f32,
    font_path: Cow<'static, str>,
    debug: bool,
}

impl OptionsBuilder {
    fn new() -> OptionsBuilder {
        OptionsBuilder {
            base_image: None,
            annotations: Vec::new(),
            output_path: None,
            output_format: OutputFormat::Jpg,
            scale_mult: 1.0,
            font_path: default_font(),
            debug: false,
        }
    }
}

impl OptionsBuilder {
    fn build(self) -> Result<Options> {
        let input_path: PathBuf = self.base_image.as_ref().unwrap().into();
        if input_path.file_name().is_none() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::ImagePath,
                description: Cow::from(
                    "The provided image path does not appear to have a filename",
                ),
                cause: None,
            });
        }

        let output_format = self.output_format;
        let output_path = self.output_path.map(|s| s.into()).unwrap_or_else(|| {
            create_output_file_path(&input_path, output_format)
        });

        let annotations = if self.annotations.is_empty() {
            return Err(BuildOptionsError {
                kind: BuildOptionsErrorKind::Annotations,
                description: Cow::from("No annotations provided"),
                cause: None,
            });
        } else {
            self.annotations
        };

        Ok(Options {
            base_image: Resource::new(self.base_image.unwrap()),
            annotations,
            output_path,
            output_format,
            scale_mult: self.scale_mult,
            font_path: self.font_path.to_string().into(),
            debug: self.debug,
        })
    }
}

#[derive(Debug)]
pub struct BuildOptionsError {
    kind: BuildOptionsErrorKind,
    description: Cow<'static, str>,
    cause: Option<Cause>,
}

#[derive(Debug)]
enum BuildOptionsErrorKind {
    Annotations,
    ImagePath,
    ScalingMultiplier,
}

impl fmt::Display for BuildOptionsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.description)
    }
}

impl error::Error for BuildOptionsError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&error::Error> {
        self.cause.as_ref().map(|cause| cause.as_ref())
    }
}

fn read_command() -> Result<Options> {
    use clap::ArgGroup;

    let text_group = ArgGroup::with_name("text_group")
        .args(&["caption", "bottom", "top", "middle"])
        .multiple(true);

    let encoding_group = ArgGroup::with_name("enc_group").args(&["jpg", "png"]);

    let app =
        clap_app!(annatar =>
        (version: crate_version!())
        (author: crate_authors!())
        (about: crate_description!())
        (@arg image: +required "Path to an image to be annotated")
        (@arg caption: "A message to be added to the bottom of the image")
        (@arg bottom: -b --bottom +takes_value "A message to be added to the bottom of the image")
        (@arg top: -t --top +takes_value "A message to be added to the top of the image")
        (@arg middle: -m --middle +takes_value "A message to be added to the middle of the image")
        (@arg output: -o --output +takes_value
            "Sets an output path for the new image (default: <image path>/<image name>.ann.<ext>)")
        (@arg scale: -s --scale +takes_value "Sets the scale multiplier for annotations")
        (@arg font: -f --font +takes_value "Sets the path of the font to be used (default: Impact)")
        (@arg debug: -d --debug "Save edge detection ... thing to disk")
        (@arg jpg: --jpg "Set JPG mode")
        (@arg png: --png "Set PNG mode (default)")
    );

    // Much easier to set up argument groups outside macro.
    let app = app.group(text_group).group(encoding_group);

    let matches = app.get_matches();
    let mut options = OptionsBuilder::new();

    options.base_image = Some(matches.value_of("image").unwrap().to_string());
    options.output_path = matches.value_of("output").map(|s| s.to_string());

    if let Some(scale_multiplier) = matches.value_of("scale") {
        let multiplier = scale_multiplier.parse::<f32>().map_err(|e| {
            BuildOptionsError {
                kind: BuildOptionsErrorKind::ScalingMultiplier,
                description: Cow::from("Scaling multiplier must be a decimal value"),
                cause: Some(Box::new(e)),
            }
        })?;
        options.scale_mult = multiplier;
    }

    if let Some(font_path) = matches.value_of("font") {
        options.font_path = Cow::from(font_path.to_string());
    }

    if let Some(caption) = matches.value_of("caption") {
        options.annotations.push(Annotation::bottom(caption));
    }

    if let Some(caption) = matches.value_of("top") {
        options.annotations.push(Annotation::top(caption));
    }

    if let Some(caption) = matches.value_of("middle") {
        options.annotations.push(Annotation::middle(caption));
    }

    if let Some(caption) = matches.value_of("bottom") {
        options.annotations.push(Annotation::bottom(caption));
    }

    options.output_format = {
        if matches.is_present("jpg") {
            OutputFormat::Jpg
        } else {
            OutputFormat::Png
        }
    };

    options.debug = matches.is_present("debug");

    options.build()
}

#[cfg(target_os = "windows")]
fn default_font() -> Cow<'static, str> {
    Cow::from("C:/Windows/Fonts/Impact.ttf")
}

#[cfg(target_os = "macos")]
fn default_font() -> Cow<'static, str> {
    Cow::from("/Library/Fonts/Impact.ttf")
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
fn default_font() -> Cow<'static, str> {
    unimplemented!("Honestly, getting a font on Linux is going to be an adventure.");
}

fn create_output_file_path(input_path: &Path, output_format: OutputFormat) -> PathBuf {
    // I unwrap this because clap already converted it to a string, implying it's valid utf-8.
    let mut file_name = input_path
        .file_name()
        .unwrap()
        .to_str()
        .unwrap()
        .to_string();

    if let Some(last_segment_idx) = file_name.rfind('.') {
        file_name.truncate(last_segment_idx);
    }

    match output_format {
        OutputFormat::Png => file_name.push_str("-annotated.png"),
        _ => file_name.push_str("-annotated.jpg"),
    }

    file_name.into()
}
<|endoftext|>


<commit_before>use std::fmt;
use ansi_term::Colour;

#[derive(Debug)]
pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

pub struct Error(pub (ErrorLevel, &'static str));

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let colour_level = match (self.0).0 {
            ErrorLevel::Info => Colour::Yellow.paint("Info"),

            // ansi_term does not have orange for some reason
            ErrorLevel::Warning => Colour::RGB(255, 210, 0).paint("Warning"),
            ErrorLevel::Error => Colour::Red.paint("Error"),
        };

        write!(f, "[{:?}] {}\n", colour_level, (self.0).1)
    }
}

// Use a hashmap which contains an error level and a string
pub struct ErrorStack(pub Vec<Error>);

impl fmt::Display for ErrorStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.0 {
            // TODO: Remove this unwrap
            write!(f, "{}\n", error).unwrap();
        }

        write!(f, "")
    }
}
<commit_msg>Remove debug print<commit_after>use std::fmt;
use ansi_term::Colour;

#[derive(Debug)]
pub enum ErrorLevel {
    Info,
    Warning,
    Error,
}

pub struct Error(pub (ErrorLevel, &'static str));

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let colour_level = match (self.0).0 {
            ErrorLevel::Info => Colour::Yellow.paint("Info"),

            // ansi_term does not have orange for some reason
            ErrorLevel::Warning => Colour::RGB(255, 210, 0).paint("Warning"),
            ErrorLevel::Error => Colour::Red.paint("Error"),
        };

        write!(f, "[{}] {}\n", colour_level, (self.0).1)
    }
}

// Use a hashmap which contains an error level and a string
pub struct ErrorStack(pub Vec<Error>);

impl fmt::Display for ErrorStack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for error in &self.0 {
            // TODO: Remove this unwrap
            write!(f, "{}\n", error).unwrap();
        }

        write!(f, "")
    }
}
<|endoftext|>