<commit_before>extern crate rand;
use rand::Rng;
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::Write;

const CYPHERBYTES: usize = 32;

fn save(fname: &str, buffer: &[u8]) {
    let mut f = match File::create(fname) {
        Ok(file) => file,
        Err(e) => { panic!("error creating file: {}", e.description()); },
    };

    match f.write(buffer) {
        Ok(_) => return,
        Err(e) => { panic!("error writing file: {}", e.description()); },
    };
}

fn main() {
    let mut inplace: bool = false;

    let args: Vec<_> = env::args().collect();
    if (args.len() != 2 && args.len() != 3) ||
       (args.len() == 3 && args[1].as_str() != "-i") {
        println!("usage: {} [-i] file-to-encrypt", args[0]);
        return
    }

    if args.len() == 3 && args[1].as_str() == "-i" {
	inplace = true;
    }

    let mut csprng = match rand::OsRng::new() {
        Ok(g) => g,
        Err(e) => panic!("cyber attack detected:  {}", e)
    };

    let mut cyphertext = [0u8; CYPHERBYTES];
    csprng.fill_bytes(&mut cyphertext);

    if inplace {
        save(args[2].as_str(), &cyphertext);
    } else {
        for x in &cyphertext { print!("{:02x}", *x); }
        println!("");
    }
}
<commit_msg>major update<commit_after>extern crate rand;
use rand::Rng;
use std::env;
use std::fs::File;
use std::io::Write;

const CYPHERBYTES: usize = 32;

fn save(fname: &str, buffer: &[u8]) {
    let mut f = match File::create(fname) {
        Ok(file) => file,
        Err(e) => { panic!("error creating file: {}", e.to_string()); },
    };

    match f.write(buffer) {
        Ok(_) => return,
        Err(e) => { panic!("error writing file: {}", e.to_string()); },
    };
}

fn main() {
    let mut inplace: bool = false;

    let args: Vec<_> = env::args().collect();
    if (args.len() != 2 && args.len() != 3) ||
       (args.len() == 3 && args[1].as_str() != "-i") {
        println!("usage: {} [-i] file-to-encrypt", args[0]);
        return
    }

    if args.len() == 3 && args[1].as_str() == "-i" {
	inplace = true;
    }

    let mut csprng = match rand::OsRng::new() {
        Ok(g) => g,
        Err(e) => panic!("cyber attack detected:  {}", e)
    };

    let mut cyphertext = [0u8; CYPHERBYTES];
    csprng.fill_bytes(&mut cyphertext);

    if inplace {
        save(args[2].as_str(), &cyphertext);
    } else {
        for x in &cyphertext { print!("{:02x}", *x); }
        println!("");
    }
}
<|endoftext|>


<commit_before>use codegen::RustWriter;
use std::io;
use std::str;
mod codegen;

struct Grammar {
	rules: ~[~Rule]
}

enum Item {
	Rule
}

struct Rule {
	name: ~str,
	expr: ~Expr,
}

enum Attr {
	AttrDefineParser(~str),
	AttrDefineType(~str)
}

struct CharSetCase {
	start: char,
	end: char
}

enum Expr {
	AnyCharExpr,
	LiteralExpr(~str),
	CharSetExpr(bool, ~[CharSetCase]),
	RuleExpr(~str),
	SequenceExpr(~[~Expr]),
	ChoiceExpr(~[~Expr]),
	OptionalExpr(~Expr),
	RepeatExpr(~Expr),
	RepeatOneExpr(~Expr),
	DelimitedExpr(~Expr, ~Expr),
	PosAssertExpr(~Expr),
	NegAssertExpr(~Expr),
	StringifyExpr(~Expr),
}

fn compile_grammar(w: &RustWriter, grammar: &Grammar) {
	compile_header(w);

	for rule in grammar.rules.iter() {
		compile_rule(w, *rule);
	}
}

fn compile_header(w: &RustWriter) {
	w.write("// Generated by rust-peg. Do not edit.
use std::str::{CharRange};

#[inline]
fn slice_eq(input: &str, pos: uint, m: &str) -> Result<uint, uint> {
    let l = m.len();
    if (input.len() >= pos + l && input.slice(pos, pos+l) == m) {
        Ok(pos+l)
    } else {
        Err(pos)
    }
}

#[inline]
fn any_char(input: &str, pos: uint) -> Result<uint, uint> {
    if input.len() > pos {
        Ok(input.char_range_at(pos).next)
    } else {
        Err(pos)
    }
}
");
}

fn compile_rule(w: &RustWriter, rule: &Rule) {
	do w.def_fn(false, "parse_"+rule.name, "input: &str, pos: uint", "Result<uint, uint>") {
		compile_expr(w, rule.expr);
	}
}

fn compile_expr(w: &RustWriter, e: &Expr) {
	match *e {
		AnyCharExpr => {
			w.line("any_char(input, pos)");
			/*w.if_else("input.len() > pos",
				||{ w.line("Ok(pos+1)"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		LiteralExpr(ref s) => {
			w.line("slice_eq(input, pos, \""+*s+"\")");
			/*w.if_else("slice_eq(input, pos, \""+*s+"\")",
				||{ w.line("Ok(pos+" + s.len().to_str() + ")"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		CharSetExpr(invert, ref cases) => {
			w.if_else("input.len() > pos",
				|| {
					w.line("let CharRange {ch, next} = input.char_range_at(pos);");
					do w.match_block("ch") {
						w.write_indent();
						for (i, case) in cases.iter().enumerate() {
							if i != 0 { w.write(" | "); }
							if case.start == case.end {
								w.write("'"+str::from_char(case.start)+"'");
							} else {
								w.write("'"+str::from_char(case.start)+"'..'"+str::from_char(case.end)+"'");
							}
						}
						w.write(" => { Ok(next) }\n");
						w.match_inline_case("_", "Err(pos)");
					}
				},
				|| { w.line("Err(pos)"); }
			)
		}

		RuleExpr(ref ruleName) => {
			w.line("parse_"+*ruleName+"(input, pos)");
		}

		SequenceExpr(ref exprs) => {

			fn write_seq(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do w.let_block("seq_res") {
						compile_expr(w, exprs[0]);
					}
					do w.match_block("seq_res") {
						w.match_inline_case("Err(pos)", "Err(pos)");
						do w.match_case("Ok(pos)") {
							write_seq(w, exprs.tail())
						}
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_seq(w, *exprs);
			}
		}

		ChoiceExpr(ref exprs) => {

			fn write_choice(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do w.let_block("choice_res") {
						compile_expr(w, exprs[0]);
					}
					do w.match_block("choice_res") {
						w.match_inline_case("Ok(pos)", "Ok(pos)");
						do w.match_case("Err(*)") {
							write_choice(w, exprs.tail());
						}
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_choice(w, *exprs);
			}

		}

		OptionalExpr(ref e) => {
			do w.let_block("optional_res") {
				compile_expr(w, *e);
			}
			do w.match_block("optional_res") {
				w.match_inline_case("Ok(newpos)", "Ok(newpos)");
				w.match_inline_case("Err(*)", "Ok(pos)");
			}
		}

		RepeatExpr(ref e) => {
			w.let_mut_stmt("repeat_pos", "pos");
			do w.loop_block {
				do w.let_block("step_res") {
					w.let_stmt("pos", "repeat_pos");
					compile_expr(w, *e);
				}
				do w.match_block("step_res") {
					do w.match_case("Ok(newpos)") {
						w.line("repeat_pos = newpos;");
					}
					do w.match_case("Err(*)") {
						w.line("break;");
					}
				}
			}
			w.line("Ok(repeat_pos)");
		}

		 RepeatOneExpr(ref e)
		| DelimitedExpr(ref e, _) => fail!("not implemented"),

		  StringifyExpr(*)
		| PosAssertExpr(*)
		| NegAssertExpr(*) => fail!("not implemented"),

	}
}

fn main() {
	let grammar = ~Grammar { rules: ~[
		~Rule {
			name: ~"sum",
			expr: ~SequenceExpr(~[
				~RuleExpr(~"product"),
				~OptionalExpr(
					~SequenceExpr(~[
						~LiteralExpr(~"+"),
						~RuleExpr(~"product")
						]))

				])
		},
		~Rule {
			name: ~"product",
			expr: ~SequenceExpr(~[
				~RuleExpr(~"atom"),
				~OptionalExpr(
					~SequenceExpr(~[
						~LiteralExpr(~"*"),
						~RuleExpr(~"atom")
						]))

				])
		},
		~Rule {
			name: ~"parens",
			expr: ~SequenceExpr(~[
				~LiteralExpr(~"("),
					~RuleExpr(~"sum"),
					~LiteralExpr(~")")
					])
		},
		~Rule {
			name: ~"number",
			expr: ~SequenceExpr(~[
				~CharSetExpr(false, ~[CharSetCase{start:'0', end: '9'}]),
				~RepeatExpr(
					~CharSetExpr(false, ~[CharSetCase{start:'0', end: '9'}]))

				])
		},
		~Rule {
			name: ~"atom",
			expr: ~ChoiceExpr(~[
				~RuleExpr(~"number"),
				~RuleExpr(~"parens")
				])
		}
		]
	}
	;

	let w = RustWriter::new(io::stdout());
	compile_grammar(&w, grammar);
}<commit_msg>Add support for RepeatOne (`+`) rules"<commit_after>use codegen::RustWriter;
use std::io;
use std::str;
mod codegen;

struct Grammar {
	rules: ~[~Rule]
}

enum Item {
	Rule
}

struct Rule {
	name: ~str,
	expr: ~Expr,
}

enum Attr {
	AttrDefineParser(~str),
	AttrDefineType(~str)
}

struct CharSetCase {
	start: char,
	end: char
}

enum Expr {
	AnyCharExpr,
	LiteralExpr(~str),
	CharSetExpr(bool, ~[CharSetCase]),
	RuleExpr(~str),
	SequenceExpr(~[~Expr]),
	ChoiceExpr(~[~Expr]),
	OptionalExpr(~Expr),
	RepeatExpr(~Expr),
	RepeatOneExpr(~Expr),
	DelimitedExpr(~Expr, ~Expr),
	PosAssertExpr(~Expr),
	NegAssertExpr(~Expr),
	StringifyExpr(~Expr),
}

fn compile_grammar(w: &RustWriter, grammar: &Grammar) {
	compile_header(w);

	for rule in grammar.rules.iter() {
		compile_rule(w, *rule);
	}
}

fn compile_header(w: &RustWriter) {
	w.write("// Generated by rust-peg. Do not edit.
use std::str::{CharRange};

#[inline]
fn slice_eq(input: &str, pos: uint, m: &str) -> Result<uint, uint> {
    let l = m.len();
    if (input.len() >= pos + l && input.slice(pos, pos+l) == m) {
        Ok(pos+l)
    } else {
        Err(pos)
    }
}

#[inline]
fn any_char(input: &str, pos: uint) -> Result<uint, uint> {
    if input.len() > pos {
        Ok(input.char_range_at(pos).next)
    } else {
        Err(pos)
    }
}
");
}

fn compile_rule(w: &RustWriter, rule: &Rule) {
	do w.def_fn(false, "parse_"+rule.name, "input: &str, pos: uint", "Result<uint, uint>") {
		compile_expr(w, rule.expr);
	}
}

fn compile_match_and_then(w: &RustWriter, e: &Expr, then: &fn()) {
	do w.let_block("seq_res") {
		compile_expr(w, e);
	}
	do w.match_block("seq_res") {
		w.match_inline_case("Err(pos)", "Err(pos)");
		do w.match_case("Ok(pos)") {
			then();
		}
	}
}

fn compile_zero_or_more(w: &RustWriter, e: &Expr) {
	w.let_mut_stmt("repeat_pos", "pos");
	do w.loop_block {
		do w.let_block("step_res") {
			w.let_stmt("pos", "repeat_pos");
			compile_expr(w, e);
		}
		do w.match_block("step_res") {
			do w.match_case("Ok(newpos)") {
				w.line("repeat_pos = newpos;");
			}
			do w.match_case("Err(*)") {
				w.line("break;");
			}
		}
	}
	w.line("Ok(repeat_pos)");
}

fn compile_expr(w: &RustWriter, e: &Expr) {
	match *e {
		AnyCharExpr => {
			w.line("any_char(input, pos)");
			/*w.if_else("input.len() > pos",
				||{ w.line("Ok(pos+1)"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		LiteralExpr(ref s) => {
			w.line("slice_eq(input, pos, \""+*s+"\")");
			/*w.if_else("slice_eq(input, pos, \""+*s+"\")",
				||{ w.line("Ok(pos+" + s.len().to_str() + ")"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		CharSetExpr(invert, ref cases) => {
			w.if_else("input.len() > pos",
				|| {
					w.line("let CharRange {ch, next} = input.char_range_at(pos);");
					do w.match_block("ch") {
						w.write_indent();
						for (i, case) in cases.iter().enumerate() {
							if i != 0 { w.write(" | "); }
							if case.start == case.end {
								w.write("'"+str::from_char(case.start)+"'");
							} else {
								w.write("'"+str::from_char(case.start)+"'..'"+str::from_char(case.end)+"'");
							}
						}
						w.write(" => { Ok(next) }\n");
						w.match_inline_case("_", "Err(pos)");
					}
				},
				|| { w.line("Err(pos)"); }
			)
		}

		RuleExpr(ref ruleName) => {
			w.line("parse_"+*ruleName+"(input, pos)");
		}

		SequenceExpr(ref exprs) => {
			fn write_seq(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do compile_match_and_then(w, exprs[0]) {
						write_seq(w, exprs.tail());
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_seq(w, *exprs);
			}
		}

		ChoiceExpr(ref exprs) => {
			fn write_choice(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0]);
				} else {
					do w.let_block("choice_res") {
						compile_expr(w, exprs[0]);
					}
					do w.match_block("choice_res") {
						w.match_inline_case("Ok(pos)", "Ok(pos)");
						do w.match_case("Err(*)") {
							write_choice(w, exprs.tail());
						}
					}
				}
			}

			if (exprs.len() > 0 ) {
				write_choice(w, *exprs);
			}
		}

		OptionalExpr(ref e) => {
			do w.let_block("optional_res") {
				compile_expr(w, *e);
			}
			do w.match_block("optional_res") {
				w.match_inline_case("Ok(newpos)", "Ok(newpos)");
				w.match_inline_case("Err(*)", "Ok(pos)");
			}
		}

		RepeatExpr(ref e) => {
			compile_zero_or_more(w, *e);
		}

		RepeatOneExpr(ref e) => {
			do compile_match_and_then(w, *e) {
				compile_zero_or_more(w, *e);
			}
		}

		DelimitedExpr(ref e, _) => fail!("not implemented"),

		  StringifyExpr(*)
		| PosAssertExpr(*)
		| NegAssertExpr(*) => fail!("not implemented"),

	}
}

fn main() {
	let grammar = ~Grammar { rules: ~[
		~Rule {
			name: ~"sum",
			expr: ~SequenceExpr(~[
				~RuleExpr(~"product"),
				~OptionalExpr(
					~SequenceExpr(~[
						~LiteralExpr(~"+"),
						~RuleExpr(~"product")
						]))

				])
		},
		~Rule {
			name: ~"product",
			expr: ~SequenceExpr(~[
				~RuleExpr(~"atom"),
				~OptionalExpr(
					~SequenceExpr(~[
						~LiteralExpr(~"*"),
						~RuleExpr(~"atom")
						]))

				])
		},
		~Rule {
			name: ~"parens",
			expr: ~SequenceExpr(~[
				~LiteralExpr(~"("),
					~RuleExpr(~"sum"),
					~LiteralExpr(~")")
					])
		},
		~Rule {
			name: ~"number",
			expr: ~RepeatOneExpr(
				~CharSetExpr(false, ~[CharSetCase{start:'0', end: '9'}]))

		},
		~Rule {
			name: ~"atom",
			expr: ~ChoiceExpr(~[
				~RuleExpr(~"number"),
				~RuleExpr(~"parens")
				])
		}
		]
	}
	;

	let w = RustWriter::new(io::stdout());
	compile_grammar(&w, grammar);
}<|endoftext|>


<commit_before>// A program that initializes the TUI and adds lines in the file (given as first argument) to it,
// with a draw() call after every line added.
//
// After adding all lines the program just quits.
//
// Useful for benchmarking TUI::draw().

use libtiny_tui::TUI;
use libtiny_ui::*;
use std::fs::File;
use std::io::{BufRead, BufReader};
use tokio::runtime::current_thread::Runtime;
use std::path::PathBuf;

fn main() {
    let args = std::env::args().collect::<Vec<_>>();
    let file_path = &args[1];
    let file = File::open(file_path).unwrap();
    let file_buffered = BufReader::new(file);
    let lines = file_buffered.lines().map(Result::unwrap).collect();

    let mut executor = Runtime::new().unwrap();
    let (tui, _) = TUI::run(PathBuf::from("../tiny/config.yml"), &mut executor);

    tui.new_server_tab("test");
    tui.draw();

    executor.block_on(bench_task(tui, lines));

    // executor.run();
}

async fn bench_task(tui: TUI, lines: Vec<String>) {
    let msg_target = MsgTarget::Server { serv: "test" };
    let time = time::now();

    for line in &lines {
        tui.add_privmsg("server", line, time, &msg_target, false, false);
        tui.draw();
    }
}
<commit_msg>libtiny_tui: Use the default tab in bench example<commit_after>// A program that initializes the TUI and adds lines in the file (given as first argument) to it,
// with a draw() call after every line added.
//
// After adding all lines the program just quits.
//
// Useful for benchmarking TUI::draw().

use libtiny_tui::TUI;
use libtiny_ui::*;
use std::fs::File;
use std::io::{BufRead, BufReader};
use tokio::runtime::current_thread::Runtime;
use std::path::PathBuf;

fn main() {
    let args = std::env::args().collect::<Vec<_>>();
    let file_path = &args[1];
    let file = File::open(file_path).unwrap();
    let file_buffered = BufReader::new(file);
    let lines = file_buffered.lines().map(Result::unwrap).collect();

    let mut executor = Runtime::new().unwrap();
    let (tui, _) = TUI::run(PathBuf::from("../tiny/config.yml"), &mut executor);

    tui.draw();

    executor.block_on(bench_task(tui, lines));

    // executor.run();
}

async fn bench_task(tui: TUI, lines: Vec<String>) {
    let msg_target = MsgTarget::Server { serv: "mentions" };
    let time = time::now();

    for line in &lines {
        tui.add_privmsg("server", line, time, &msg_target, false, false);
        tui.draw();
    }
}
<|endoftext|>


<commit_before>use crate::{internal_rpc::InternalRPCHandle, Promise, Result};
use async_task::Task;
use crossbeam_channel::{Receiver, Sender};
use parking_lot::Mutex;
use std::{
    fmt,
    future::Future,
    ops::Deref,
    pin::Pin,
    sync::Arc,
    thread::{Builder as ThreadBuilder, JoinHandle},
};

pub trait Executor: std::fmt::Debug + Send + Sync {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()>;
}

pub(crate) trait ExecutorExt: Executor {
    fn spawn_internal(&self, promise: Promise<()>, internal_rpc: InternalRPCHandle) -> Result<()> {
        if let Some(res) = promise.try_wait() {
            if let Err(err) = res.clone() {
                internal_rpc.set_connection_error(err);
            }
            res
        } else {
            self.spawn(Box::pin(async move {
                if let Err(err) = promise.await {
                    internal_rpc.set_connection_error(err);
                }
            }))
        }
    }
}

impl Executor for Arc<dyn Executor> {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()> {
        self.deref().spawn(f)
    }
}
impl ExecutorExt for Arc<dyn Executor> {}

#[derive(Clone)]
pub struct DefaultExecutor {
    sender: Sender<Task<()>>,
    receiver: Receiver<Task<()>>,
    threads: Arc<Mutex<Vec<JoinHandle<()>>>>,
    max_threads: usize,
}

impl DefaultExecutor {
    pub fn new(max_threads: usize) -> Self {
        let (sender, receiver) = crossbeam_channel::unbounded();
        let threads = Default::default();
        Self {
            sender,
            receiver,
            threads,
            max_threads,
        }
    }

    pub(crate) fn maybe_spawn_thread(&self) -> Result<()> {
        let mut threads = self.threads.lock();
        let id = threads.len() + 1;
        if id <= self.max_threads {
            let receiver = self.receiver.clone();
            threads.push(
                ThreadBuilder::new()
                    .name(format!("executor {}", id))
                    .spawn(move || {
                        for task in receiver {
                            task.run();
                        }
                    })?,
            );
        }
        Ok(())
    }
}

impl Default for DefaultExecutor {
    fn default() -> Self {
        Self::new(1)
    }
}

impl fmt::Debug for DefaultExecutor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DefaultExecutor")
            .field("max_threads", &self.max_threads)
            .finish()
    }
}

impl Executor for DefaultExecutor {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()> {
        self.maybe_spawn_thread()?;
        let sender = self.sender.clone();
        let schedule = move |task| sender.send(task).expect("executor failed");
        let (task, _) = async_task::spawn(f, schedule, ());
        task.schedule();
        Ok(())
    }
}
<commit_msg>executor: simplify spawn_internal<commit_after>use crate::{internal_rpc::InternalRPCHandle, Result};
use async_task::Task;
use crossbeam_channel::{Receiver, Sender};
use parking_lot::Mutex;
use std::{
    fmt,
    future::Future,
    ops::Deref,
    pin::Pin,
    sync::Arc,
    thread::{Builder as ThreadBuilder, JoinHandle},
};

pub trait Executor: std::fmt::Debug + Send + Sync {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()>;
}

pub(crate) trait ExecutorExt: Executor {
    fn spawn_internal(
        &self,
        f: impl Future<Output = Result<()>> + Send + 'static,
        internal_rpc: InternalRPCHandle,
    ) -> Result<()> {
        self.spawn(Box::pin(async move {
            if let Err(err) = f.await {
                internal_rpc.set_connection_error(err);
            }
        }))
    }
}

impl Executor for Arc<dyn Executor> {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()> {
        self.deref().spawn(f)
    }
}
impl ExecutorExt for Arc<dyn Executor> {}

#[derive(Clone)]
pub struct DefaultExecutor {
    sender: Sender<Task<()>>,
    receiver: Receiver<Task<()>>,
    threads: Arc<Mutex<Vec<JoinHandle<()>>>>,
    max_threads: usize,
}

impl DefaultExecutor {
    pub fn new(max_threads: usize) -> Self {
        let (sender, receiver) = crossbeam_channel::unbounded();
        let threads = Default::default();
        Self {
            sender,
            receiver,
            threads,
            max_threads,
        }
    }

    pub(crate) fn maybe_spawn_thread(&self) -> Result<()> {
        let mut threads = self.threads.lock();
        let id = threads.len() + 1;
        if id <= self.max_threads {
            let receiver = self.receiver.clone();
            threads.push(
                ThreadBuilder::new()
                    .name(format!("executor {}", id))
                    .spawn(move || {
                        for task in receiver {
                            task.run();
                        }
                    })?,
            );
        }
        Ok(())
    }
}

impl Default for DefaultExecutor {
    fn default() -> Self {
        Self::new(1)
    }
}

impl fmt::Debug for DefaultExecutor {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("DefaultExecutor")
            .field("max_threads", &self.max_threads)
            .finish()
    }
}

impl Executor for DefaultExecutor {
    fn spawn(&self, f: Pin<Box<dyn Future<Output = ()> + Send>>) -> Result<()> {
        self.maybe_spawn_thread()?;
        let sender = self.sender.clone();
        let schedule = move |task| sender.send(task).expect("executor failed");
        let (task, _) = async_task::spawn(f, schedule, ());
        task.schedule();
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

#[cfg(feature = "glfw-sys")]
#[link(name = "glfw3", kind = "static")]
extern {}

#[cfg(not(feature = "glfw-sys"))]
// leaving off `kind = static` allows for the specification of a dynamic library if desired
#[cfg(target_family = "unix")]
#[link(name = "glfw")]
extern {}

#[cfg(not(feature = "glfw-sys"))]
#[cfg(target_family = "windows")]
#[link(name = "glfw3")]
extern {}

#[cfg(target_os="windows")]
#[link(name = "opengl32")]
#[link(name = "gdi32")]
#[link(name = "user32")]
#[link(name = "shell32")]
extern {}

#[cfg(any(target_os="linux", target_os="freebsd", target_os="dragonfly"))]
#[link(name = "X11")]
#[link(name = "GL")]
#[link(name = "Xxf86vm")]
#[link(name = "Xrandr")]
#[link(name = "Xi")]
#[link(name = "Xcursor")]
#[link(name = "Xinerama")]
extern {}

#[cfg(target_os="macos")]
#[link(name = "Cocoa", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "IOKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
extern {}
<commit_msg>Remove libs that are no longer linked on linux<commit_after>// Copyright 2014 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

#[cfg(feature = "glfw-sys")]
#[link(name = "glfw3", kind = "static")]
extern {}

#[cfg(not(feature = "glfw-sys"))]
// leaving off `kind = static` allows for the specification of a dynamic library if desired
#[cfg(target_family = "unix")]
#[link(name = "glfw")]
extern {}

#[cfg(not(feature = "glfw-sys"))]
#[cfg(target_family = "windows")]
#[link(name = "glfw3")]
extern {}

#[cfg(target_os="windows")]
#[link(name = "opengl32")]
#[link(name = "gdi32")]
#[link(name = "user32")]
#[link(name = "shell32")]
extern {}

#[cfg(any(target_os="linux", target_os="freebsd", target_os="dragonfly"))]
#[link(name = "X11")]
extern {}

#[cfg(target_os="macos")]
#[link(name = "Cocoa", kind = "framework")]
#[link(name = "OpenGL", kind = "framework")]
#[link(name = "IOKit", kind = "framework")]
#[link(name = "CoreFoundation", kind = "framework")]
#[link(name = "QuartzCore", kind = "framework")]
extern {}
<|endoftext|>


<commit_before>use libc::{self, c_char, c_void, size_t};

use std::mem;
use std::ptr;

use num::{Integer, NumCast};
use num::traits::cast;
use std::ffi::{CStr, CString};

use error::H5Result;

pub fn string_from_cstr(string: *const c_char) -> String {
    unsafe {
        String::from_utf8_unchecked(CStr::from_ptr(string).to_bytes().to_vec())
    }
}

pub fn string_to_cstr<S>(string: S) -> *const c_char where S: Into<String> {
    unsafe {
        CString::from_vec_unchecked(string.into().into_bytes()).as_ptr()
    }
}

#[test]
fn test_string_cstr() {
    let s1: String = "foo".to_string();
    assert_eq!(s1, string_from_cstr(string_to_cstr(s1.clone())));
    let s2: &str = "bar";
    assert_eq!(s2, string_from_cstr(string_to_cstr(s2)));
}

pub fn get_h5_str<T, F>(func: F) -> H5Result<String>
                 where F: Fn(*mut c_char, size_t) -> T, T: Integer + NumCast {
    unsafe {
        let len: isize = 1 + cast::<T, isize>(func(ptr::null_mut(), 0)).unwrap();
        ensure!(len > 0, "negative string length in get_h5_str()");
        let buf = libc::malloc((len as size_t) * mem::size_of::<c_char>() as size_t) as *mut c_char;
        func(buf, len as size_t);
        let msg = string_from_cstr(buf);
        libc::free(buf as *mut c_void);
        Ok(msg)
    }
}

#[test]
pub fn test_get_h5_str() {
    use ffi::h5e::{H5E_type_t, H5Eget_msg, H5E_CANTOPENOBJ};
    let s = unsafe {
        get_h5_str(|msg, size| {
            H5Eget_msg(*H5E_CANTOPENOBJ, ptr::null_mut(), msg, size)
        }).ok().unwrap()
    };
    assert_eq!(s, "Can't open object");
}
<commit_msg>Remove unused import<commit_after>use libc::{self, c_char, c_void, size_t};

use std::mem;
use std::ptr;

use num::{Integer, NumCast};
use num::traits::cast;
use std::ffi::{CStr, CString};

use error::H5Result;

pub fn string_from_cstr(string: *const c_char) -> String {
    unsafe {
        String::from_utf8_unchecked(CStr::from_ptr(string).to_bytes().to_vec())
    }
}

pub fn string_to_cstr<S>(string: S) -> *const c_char where S: Into<String> {
    unsafe {
        CString::from_vec_unchecked(string.into().into_bytes()).as_ptr()
    }
}

#[test]
fn test_string_cstr() {
    let s1: String = "foo".to_string();
    assert_eq!(s1, string_from_cstr(string_to_cstr(s1.clone())));
    let s2: &str = "bar";
    assert_eq!(s2, string_from_cstr(string_to_cstr(s2)));
}

pub fn get_h5_str<T, F>(func: F) -> H5Result<String>
                 where F: Fn(*mut c_char, size_t) -> T, T: Integer + NumCast {
    unsafe {
        let len: isize = 1 + cast::<T, isize>(func(ptr::null_mut(), 0)).unwrap();
        ensure!(len > 0, "negative string length in get_h5_str()");
        let buf = libc::malloc((len as size_t) * mem::size_of::<c_char>() as size_t) as *mut c_char;
        func(buf, len as size_t);
        let msg = string_from_cstr(buf);
        libc::free(buf as *mut c_void);
        Ok(msg)
    }
}

#[test]
pub fn test_get_h5_str() {
    use ffi::h5e::{H5Eget_msg, H5E_CANTOPENOBJ};
    let s = unsafe {
        get_h5_str(|msg, size| {
            H5Eget_msg(*H5E_CANTOPENOBJ, ptr::null_mut(), msg, size)
        }).ok().unwrap()
    };
    assert_eq!(s, "Can't open object");
}
<|endoftext|>


<commit_before>use super::*;

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ConstExpr {
    /// A function call
    ///
    /// The first field resolves to the function itself,
    /// and the second field is the list of arguments
    Call(Box<ConstExpr>, Vec<ConstExpr>),
    /// A binary operation (For example: `a + b`, `a * b`)
    Binary(BinOp, Box<ConstExpr>, Box<ConstExpr>),
    /// A unary operation (For example: `!x`, `*x`)
    Unary(UnOp, Box<ConstExpr>),
    /// A literal (For example: `1`, `"foo"`)
    Lit(Lit),
    /// A cast (`foo as f64`)
    Cast(Box<ConstExpr>, Box<Ty>),
    /// Variable reference, possibly containing `::` and/or type
    /// parameters, e.g. foo::bar::<baz>.
    Path(Path),
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use {BinOp, Ty};
    use lit::parsing::lit;
    use op::parsing::{binop, unop};
    use ty::parsing::{path, ty};

    named!(pub const_expr -> ConstExpr, do_parse!(
        mut e: alt!(
            expr_lit
            |
            expr_unary
            |
            path => { ConstExpr::Path }
        ) >>
        many0!(alt!(
            tap!(args: and_call => {
                e = ConstExpr::Call(Box::new(e), args);
            })
            |
            tap!(more: and_binary => {
                let (op, other) = more;
                e = ConstExpr::Binary(op, Box::new(e), Box::new(other));
            })
            |
            tap!(ty: and_cast => {
                e = ConstExpr::Cast(Box::new(e), Box::new(ty));
            })
        )) >>
        (e)
    ));

    named!(and_call -> Vec<ConstExpr>, do_parse!(
        punct!("(") >>
        args: terminated_list!(punct!(","), const_expr) >>
        punct!(")") >>
        (args)
    ));

    named!(and_binary -> (BinOp, ConstExpr), tuple!(binop, const_expr));

    named!(expr_unary -> ConstExpr, do_parse!(
        operator: unop >>
        operand: const_expr >>
        (ConstExpr::Unary(operator, Box::new(operand)))
    ));

    named!(expr_lit -> ConstExpr, map!(lit, ConstExpr::Lit));

    named!(and_cast -> Ty, do_parse!(
        keyword!("as") >>
        ty: ty >>
        (ty)
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};

    impl ToTokens for ConstExpr {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                ConstExpr::Call(ref func, ref args) => {
                    func.to_tokens(tokens);
                    tokens.append("(");
                    tokens.append_separated(args, ",");
                    tokens.append(")");
                }
                ConstExpr::Binary(op, ref left, ref right) => {
                    left.to_tokens(tokens);
                    op.to_tokens(tokens);
                    right.to_tokens(tokens);
                }
                ConstExpr::Unary(op, ref expr) => {
                    op.to_tokens(tokens);
                    expr.to_tokens(tokens);
                }
                ConstExpr::Lit(ref lit) => lit.to_tokens(tokens),
                ConstExpr::Cast(ref expr, ref ty) => {
                    expr.to_tokens(tokens);
                    tokens.append("as");
                    ty.to_tokens(tokens);
                }
                ConstExpr::Path(ref path) => path.to_tokens(tokens),
            }
        }
    }
}
<commit_msg>Parenthesized constant expressions<commit_after>use super::*;

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ConstExpr {
    /// A function call
    ///
    /// The first field resolves to the function itself,
    /// and the second field is the list of arguments
    Call(Box<ConstExpr>, Vec<ConstExpr>),
    /// A binary operation (For example: `a + b`, `a * b`)
    Binary(BinOp, Box<ConstExpr>, Box<ConstExpr>),
    /// A unary operation (For example: `!x`, `*x`)
    Unary(UnOp, Box<ConstExpr>),
    /// A literal (For example: `1`, `"foo"`)
    Lit(Lit),
    /// A cast (`foo as f64`)
    Cast(Box<ConstExpr>, Box<Ty>),
    /// Variable reference, possibly containing `::` and/or type
    /// parameters, e.g. foo::bar::<baz>.
    Path(Path),
    /// No-op: used solely so we can pretty-print faithfully
    Paren(Box<ConstExpr>),
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use {BinOp, Ty};
    use lit::parsing::lit;
    use op::parsing::{binop, unop};
    use ty::parsing::{path, ty};

    named!(pub const_expr -> ConstExpr, do_parse!(
        mut e: alt!(
            expr_unary
            |
            expr_lit
            |
            expr_path
            |
            expr_paren
        ) >>
        many0!(alt!(
            tap!(args: and_call => {
                e = ConstExpr::Call(Box::new(e), args);
            })
            |
            tap!(more: and_binary => {
                let (op, other) = more;
                e = ConstExpr::Binary(op, Box::new(e), Box::new(other));
            })
            |
            tap!(ty: and_cast => {
                e = ConstExpr::Cast(Box::new(e), Box::new(ty));
            })
        )) >>
        (e)
    ));

    named!(and_call -> Vec<ConstExpr>, do_parse!(
        punct!("(") >>
        args: terminated_list!(punct!(","), const_expr) >>
        punct!(")") >>
        (args)
    ));

    named!(and_binary -> (BinOp, ConstExpr), tuple!(binop, const_expr));

    named!(expr_unary -> ConstExpr, do_parse!(
        operator: unop >>
        operand: const_expr >>
        (ConstExpr::Unary(operator, Box::new(operand)))
    ));

    named!(expr_lit -> ConstExpr, map!(lit, ConstExpr::Lit));

    named!(expr_path -> ConstExpr, map!(path, ConstExpr::Path));

    named!(expr_paren -> ConstExpr, do_parse!(
        punct!("(") >>
        e: const_expr >>
        punct!(")") >>
        (ConstExpr::Paren(Box::new(e)))
    ));

    named!(and_cast -> Ty, do_parse!(
        keyword!("as") >>
        ty: ty >>
        (ty)
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};

    impl ToTokens for ConstExpr {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                ConstExpr::Call(ref func, ref args) => {
                    func.to_tokens(tokens);
                    tokens.append("(");
                    tokens.append_separated(args, ",");
                    tokens.append(")");
                }
                ConstExpr::Binary(op, ref left, ref right) => {
                    left.to_tokens(tokens);
                    op.to_tokens(tokens);
                    right.to_tokens(tokens);
                }
                ConstExpr::Unary(op, ref expr) => {
                    op.to_tokens(tokens);
                    expr.to_tokens(tokens);
                }
                ConstExpr::Lit(ref lit) => lit.to_tokens(tokens),
                ConstExpr::Cast(ref expr, ref ty) => {
                    expr.to_tokens(tokens);
                    tokens.append("as");
                    ty.to_tokens(tokens);
                }
                ConstExpr::Path(ref path) => path.to_tokens(tokens),
                ConstExpr::Paren(ref expr) => {
                    tokens.append("(");
                    expr.to_tokens(tokens);
                    tokens.append(")");
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::asm::directive_parsers::*;
use crate::asm::error::Error;
use crate::asm::label_parsers::*;
use crate::asm::opcode_parsers::*;
use crate::asm::operand_parsers::operand;
use crate::asm::symbols::*;
use crate::asm::Token;
use crate::vm::real_register_to_idx;

use nom::types::CompleteStr;
use std::fmt;

#[derive(Debug, PartialEq)]
pub struct AssemblerInstruction {
    // TODO: these are pub for directive_parsers. maybe private and
    // add a ctor.
    pub label: Option<Token>,
    pub directive: Option<Token>,
    pub opcode: Option<Token>,
    pub operand0: Option<Token>,
    pub operand1: Option<Token>,
    pub operand2: Option<Token>,
}

impl AssemblerInstruction {
    pub fn is_label(&self) -> bool {
        self.label.is_some()
    }

    pub fn label_name(&self) -> Option<String> {
        match &self.label {
            Some(l) => match l {
                Token::LabelDecl { name } => Some(name.clone()),
                _ => None,
            },
            None => None,
        }
    }

    pub fn is_directive(&self) -> bool {
        self.directive.is_some()
    }

    pub fn directive_name(&self) -> Option<String> {
        match &self.directive {
            Some(d) => match d {
                Token::Directive { name } => Some(name.to_string()),
                _ => None,
            },
            None => None,
        }
    }

    pub fn is_opcode(&self) -> bool {
        self.opcode.is_some()
    }

    pub fn has_operands(&self) -> bool {
        self.operand0.is_some() || self.operand1.is_some() || self.operand2.is_some()
    }

    pub fn string_constant(&self) -> Option<String> {
        match &self.operand0 {
            Some(d) => match d {
                Token::DoString { value } => Some(value.to_string()),
                _ => None,
            },
            None => None,
        }
    }

    pub fn to_bytes(&self, symbols: &Table) -> Result<Vec<u8>, Error> {
        let mut results = vec![];
        if let Some(ref token) = self.opcode {
            match token {
                Token::Op { code } => {
                    let b: u8 = (*code).into();
                    results.push(b);
                }
                _ => return Err(Error::NotAnOpcode),
            }
        };

        for operand in &[&self.operand0, &self.operand1, &self.operand2] {
            if let Some(token) = operand {
                AssemblerInstruction::extract_operand(token, symbols, &mut results)?
            }
        }

        while results.len() < 4 {
            results.push(0);
        }

        Ok(results)
    }

    fn extract_operand(t: &Token, symbols: &Table, results: &mut Vec<u8>) -> Result<(), Error> {
        match t {
            Token::IntRegister { idx } => {
                results.push(*idx);
            }
            Token::RealRegister { idx } => {
                let idx = real_register_to_idx(*idx);
                results.push(idx);
            }
            Token::Integer { value } => {
                for b in value.to_be_bytes().iter() {
                    results.push(*b);
                }
            }
            Token::Real { value } => {
                for b in value.to_be_bytes().iter() {
                    results.push(*b);
                }
            }
            Token::LabelRef { name } => {
                if let Some(value) = symbols.value(name) {
                    let lb = value;
                    let hb = value >> 8;
                    results.push(hb as u8);
                    results.push(lb as u8);
                } else {
                    return Err(Error::UnknownLabel {
                        name: name.to_string(),
                    });
                }
            }
            _ => {
                return Err(Error::UnexpectedToken { token: t.clone() });
            }
        };
        Ok(())
    }
}

impl fmt::Display for AssemblerInstruction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "(Label: {:?}, Opcode: {:?}\nOp 0: {:?}, Op 1: {:?}, Op 2: {:?})",
            self.label, self.opcode, self.operand0, self.operand1, self.operand2
        )
    }
}

named!(pub instruction<CompleteStr, AssemblerInstruction>,
    do_parse!(
        instr: alt!(
            instruction_comb | directive
        ) >>
        (
            instr
        )
    )
);

named!(instruction_comb<CompleteStr, AssemblerInstruction>,
    do_parse!(
        l: opt!(label_decl) >>
        o: opcode >>
        o0: opt!(operand) >>
        o1: opt!(operand) >>
        o2: opt!(operand) >>
        (
            AssemblerInstruction{
                label: l,
                directive: None,
                opcode: Some(o),
                operand0: o0,
                operand1: o1,
                operand2: o2,
            }
        )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use crate::asm::Opcode;

    #[test]
    fn test_extract_operand_int_register() {
        let token = Token::IntRegister { idx: 4 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![4]);
    }

    #[test]
    fn test_extract_operand_real_register() {
        let token = Token::RealRegister { idx: 3 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![131]);
    }

    #[test]
    fn test_extract_operand_integer() {
        let token = Token::Integer { value: 42 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![0, 0, 0, 42]);

        let token = Token::Integer { value: -42 };
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![255, 255, 255, 214]);
    }

    #[test]
    fn test_extract_operand_real() {
        let token = Token::Real { value: 4.2 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![64, 16, 204, 204, 204, 204, 204, 205]);

        let token = Token::Real { value: -4.2 };
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![192, 16, 204, 204, 204, 204, 204, 205]);
    }

    #[test]
    fn test_parse_instruction_form_one() {
        let result = instruction_comb(CompleteStr("load $0 #100\n"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::LOAD }),
                    operand0: Some(Token::IntRegister { idx: 0 }),
                    operand1: Some(Token::Integer { value: 100 }),
                    operand2: None,
                }
            ))
        )
    }

    #[test]
    fn test_parse_instruction_form_one_with_label() {
        let result = instruction_comb(CompleteStr("load $0 @test1\n"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::LOAD }),
                    operand0: Some(Token::IntRegister { idx: 0 }),
                    operand1: Some(Token::LabelRef {
                        name: "test1".to_string()
                    }),
                    operand2: None,
                }
            ))
        )
    }

    #[test]
    fn test_parse_instruction_form_two() {
        let result = instruction_comb(CompleteStr("halt"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::HLT }),
                    operand0: None,
                    operand1: None,
                    operand2: None,
                }
            ))
        );
    }

    #[test]
    fn test_parse_instruction_form_three() {
        let result = instruction_comb(CompleteStr("add %0 $1 $2\n"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::ADD }),
                    operand0: Some(Token::RealRegister { idx: 0 }),
                    operand1: Some(Token::IntRegister { idx: 1 }),
                    operand2: Some(Token::IntRegister { idx: 2 }),
                }
            ))
        );
    }
}
<commit_msg>better todo<commit_after>use crate::asm::directive_parsers::*;
use crate::asm::error::Error;
use crate::asm::label_parsers::*;
use crate::asm::opcode_parsers::*;
use crate::asm::operand_parsers::operand;
use crate::asm::symbols::*;
use crate::asm::Token;
use crate::vm::real_register_to_idx;

use nom::types::CompleteStr;
use std::fmt;

#[derive(Debug, PartialEq)]
// TODO: Split this into Instructions, Directives, and Labels and clean up all the type safety bits.
pub struct AssemblerInstruction {
    pub label: Option<Token>,
    pub directive: Option<Token>,
    pub opcode: Option<Token>,
    pub operand0: Option<Token>,
    pub operand1: Option<Token>,
    pub operand2: Option<Token>,
}

impl AssemblerInstruction {
    pub fn is_label(&self) -> bool {
        self.label.is_some()
    }

    pub fn label_name(&self) -> Option<String> {
        match &self.label {
            Some(l) => match l {
                Token::LabelDecl { name } => Some(name.clone()),
                _ => None,
            },
            None => None,
        }
    }

    pub fn is_directive(&self) -> bool {
        self.directive.is_some()
    }

    pub fn directive_name(&self) -> Option<String> {
        match &self.directive {
            Some(d) => match d {
                Token::Directive { name } => Some(name.to_string()),
                _ => None,
            },
            None => None,
        }
    }

    pub fn is_opcode(&self) -> bool {
        self.opcode.is_some()
    }

    pub fn has_operands(&self) -> bool {
        self.operand0.is_some() || self.operand1.is_some() || self.operand2.is_some()
    }

    pub fn string_constant(&self) -> Option<String> {
        match &self.operand0 {
            Some(d) => match d {
                Token::DoString { value } => Some(value.to_string()),
                _ => None,
            },
            None => None,
        }
    }

    pub fn to_bytes(&self, symbols: &Table) -> Result<Vec<u8>, Error> {
        let mut results = vec![];
        if let Some(ref token) = self.opcode {
            match token {
                Token::Op { code } => {
                    let b: u8 = (*code).into();
                    results.push(b);
                }
                _ => return Err(Error::NotAnOpcode),
            }
        };

        for operand in &[&self.operand0, &self.operand1, &self.operand2] {
            if let Some(token) = operand {
                AssemblerInstruction::extract_operand(token, symbols, &mut results)?
            }
        }

        while results.len() < 4 {
            results.push(0);
        }

        Ok(results)
    }

    fn extract_operand(t: &Token, symbols: &Table, results: &mut Vec<u8>) -> Result<(), Error> {
        match t {
            Token::IntRegister { idx } => {
                results.push(*idx);
            }
            Token::RealRegister { idx } => {
                let idx = real_register_to_idx(*idx);
                results.push(idx);
            }
            Token::Integer { value } => {
                for b in value.to_be_bytes().iter() {
                    results.push(*b);
                }
            }
            Token::Real { value } => {
                for b in value.to_be_bytes().iter() {
                    results.push(*b);
                }
            }
            Token::LabelRef { name } => {
                if let Some(value) = symbols.value(name) {
                    let lb = value;
                    let hb = value >> 8;
                    results.push(hb as u8);
                    results.push(lb as u8);
                } else {
                    return Err(Error::UnknownLabel {
                        name: name.to_string(),
                    });
                }
            }
            _ => {
                return Err(Error::UnexpectedToken { token: t.clone() });
            }
        };
        Ok(())
    }
}

impl fmt::Display for AssemblerInstruction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "(Label: {:?}, Opcode: {:?}\nOp 0: {:?}, Op 1: {:?}, Op 2: {:?})",
            self.label, self.opcode, self.operand0, self.operand1, self.operand2
        )
    }
}

named!(pub instruction<CompleteStr, AssemblerInstruction>,
    do_parse!(
        instr: alt!(
            instruction_comb | directive
        ) >>
        (
            instr
        )
    )
);

named!(instruction_comb<CompleteStr, AssemblerInstruction>,
    do_parse!(
        l: opt!(label_decl) >>
        o: opcode >>
        o0: opt!(operand) >>
        o1: opt!(operand) >>
        o2: opt!(operand) >>
        (
            AssemblerInstruction{
                label: l,
                directive: None,
                opcode: Some(o),
                operand0: o0,
                operand1: o1,
                operand2: o2,
            }
        )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use crate::asm::Opcode;

    #[test]
    fn test_extract_operand_int_register() {
        let token = Token::IntRegister { idx: 4 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![4]);
    }

    #[test]
    fn test_extract_operand_real_register() {
        let token = Token::RealRegister { idx: 3 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![131]);
    }

    #[test]
    fn test_extract_operand_integer() {
        let token = Token::Integer { value: 42 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![0, 0, 0, 42]);

        let token = Token::Integer { value: -42 };
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![255, 255, 255, 214]);
    }

    #[test]
    fn test_extract_operand_real() {
        let token = Token::Real { value: 4.2 };
        let symbols = Table::new();
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![64, 16, 204, 204, 204, 204, 204, 205]);

        let token = Token::Real { value: -4.2 };
        let mut results = vec![];

        assert!(AssemblerInstruction::extract_operand(&token, &symbols, &mut results).is_ok());
        assert_eq!(results, vec![192, 16, 204, 204, 204, 204, 204, 205]);
    }

    #[test]
    fn test_parse_instruction_form_one() {
        let result = instruction_comb(CompleteStr("load $0 #100\n"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::LOAD }),
                    operand0: Some(Token::IntRegister { idx: 0 }),
                    operand1: Some(Token::Integer { value: 100 }),
                    operand2: None,
                }
            ))
        )
    }

    #[test]
    fn test_parse_instruction_form_one_with_label() {
        let result = instruction_comb(CompleteStr("load $0 @test1\n"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::LOAD }),
                    operand0: Some(Token::IntRegister { idx: 0 }),
                    operand1: Some(Token::LabelRef {
                        name: "test1".to_string()
                    }),
                    operand2: None,
                }
            ))
        )
    }

    #[test]
    fn test_parse_instruction_form_two() {
        let result = instruction_comb(CompleteStr("halt"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::HLT }),
                    operand0: None,
                    operand1: None,
                    operand2: None,
                }
            ))
        );
    }

    #[test]
    fn test_parse_instruction_form_three() {
        let result = instruction_comb(CompleteStr("add %0 $1 $2\n"));
        assert_eq!(
            result,
            Ok((
                CompleteStr(""),
                AssemblerInstruction {
                    label: None,
                    directive: None,
                    opcode: Some(Token::Op { code: Opcode::ADD }),
                    operand0: Some(Token::RealRegister { idx: 0 }),
                    operand1: Some(Token::IntRegister { idx: 1 }),
                    operand2: Some(Token::IntRegister { idx: 2 }),
                }
            ))
        );
    }
}
<|endoftext|>


<commit_before><commit_msg>Updated documentation<commit_after><|endoftext|>


<commit_before>use std::{env,
          ffi::OsString,
          fs as stdfs,
          path::{Path,
                 PathBuf}};

use crate::{common::ui::{UIWriter,
                         UI},
            hcore::{crypto::CACHE_KEY_PATH_ENV_VAR,
                    env as henv,
                    fs}};

use crate::{config,
            error::{Error,
                    Result},
            BLDR_URL_ENVVAR,
            ORIGIN_ENVVAR};

use habitat_core::AUTH_TOKEN_ENVVAR;
use same_file::is_same_file;

pub const ARTIFACT_PATH_ENVVAR: &str = "ARTIFACT_PATH";
pub const CERT_PATH_ENVVAR: &str = "CERT_PATH";
pub const SSL_CERT_FILE_ENVVAR: &str = "SSL_CERT_FILE";

const STUDIO_PACKAGE_IDENT: &str = "core/hab-studio";

#[derive(Clone, Copy)]
enum Sensitivity {
    PrintValue,
    NoPrintValue,
}

fn set_env_var_from_config(env_var: &str, config_val: Option<String>, sensitive: Sensitivity) {
    if henv::var(env_var).is_err() {
        if let Some(val) = config_val {
            match sensitive {
                Sensitivity::NoPrintValue => {
                    debug!("Setting {}=REDACTED (sensitive) via config file", env_var)
                }
                Sensitivity::PrintValue => debug!("Setting {}={} via config file", env_var, val),
            }
            env::set_var(env_var, val);
        }
    }
}

fn cache_ssl_cert_file(cert_file: &str, cert_cache_dir: &Path) -> Result<()> {
    let cert_path = Path::new(&cert_file);

    let cert_filename = match cert_path.file_name() {
        Some(cert_filename) => cert_filename,
        None => return Err(Error::CacheSslCertError(format!("{:?} is not a file", &cert_file))),
    };
    let cache_file = cert_cache_dir.join(&cert_filename);

    if cache_file.exists() && cert_path.exists() && is_same_file(&cache_file, &cert_path)? {
        return Err(Error::CacheSslCertError("Source and destination certificate are the same \
                                             file"
                                                  .to_string()));
    }

    debug!("Caching SSL_CERT_FILE {:?} => {:?}", cert_file, cache_file);
    stdfs::copy(cert_file, &cache_file)?;

    Ok(())
}

pub async fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
    let config = config::load()?;

    set_env_var_from_config(AUTH_TOKEN_ENVVAR,
                            config.auth_token,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(BLDR_URL_ENVVAR, config.bldr_url, Sensitivity::PrintValue);
    set_env_var_from_config(ORIGIN_ENVVAR, config.origin, Sensitivity::PrintValue);

    if config.ctl_secret.is_some() {
        ui.warn("Your Supervisor CtlGateway secret is not being copied to the Studio \
                 environment because the Studio's Supervisor is local. If you wish to contact a \
                 remote Supervisor from the Studio, please set the HAB_CTL_SECRET variable with \
                 your secret.")?;
    }

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = &*fs::CACHE_KEY_PATH;
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    let ssl_path = match henv::var(CERT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_ssl_path(None::<&str>);
            debug!("Setting {}={}", CERT_PATH_ENVVAR, path.display());
            env::set_var(CERT_PATH_ENVVAR, &path);
            path
        }
    };
    if !ssl_path.is_dir() {
        debug!("Creating ssl_path at: {}", ssl_path.display());
        stdfs::create_dir_all(&ssl_path)?;
    }

    if let Ok(ssl_cert_file) = env::var(SSL_CERT_FILE_ENVVAR) {
        if let Err(err) = cache_ssl_cert_file(&ssl_cert_file, &ssl_path) {
            warn!("Unable to cache SSL_CERT_FILE: {}", err);
        }
    }

    inner::start(ui, args).await
}

#[cfg(target_os = "linux")]
mod inner {
    use crate::{command::studio::docker,
                common::ui::{UIWriter,
                             UI},
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::{am_i_root,
                             find_command},
                        os::process,
                        package::{PackageIdent,
                                  PackageInstall},
                        users::linux as group},
                VERSION};
    use std::{env,
              ffi::OsString,
              path::PathBuf,
              str::FromStr};

    const SUDO_CMD: &str = "sudo";
    const STUDIO_CMD: &str = "hab-studio";
    const STUDIO_CMD_ENVVAR: &str = "HAB_STUDIO_BINARY";

    pub async fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
        rerun_with_sudo_if_needed(ui, &args)?;
        if is_docker_studio(&args) {
            docker::start_docker_studio(ui, args)
        } else {
            let command = match henv::var(STUDIO_CMD_ENVVAR) {
                Ok(command) => PathBuf::from(command),
                Err(_) => {
                    init()?;
                    let version: Vec<&str> = VERSION.split('/').collect();
                    let ident = PackageIdent::from_str(&format!("{}/{}",
                                                                super::STUDIO_PACKAGE_IDENT,
                                                                version[0]))?;
                    let command = exec::command_from_min_pkg(ui, STUDIO_CMD, &ident).await?;
                    // This is a duplicate of the code in `hab pkg exec` and
                    // should be refactored as part of or after:
                    // https://github.com/habitat-sh/habitat/issues/6633
                    // https://github.com/habitat-sh/habitat/issues/6634
                    let pkg_install = PackageInstall::load(&ident, None)?;
                    let cmd_env = pkg_install.environment_for_command()?;
                    for (key, value) in cmd_env.into_iter() {
                        debug!("Setting: {}='{}'", key, value);
                        env::set_var(key, value);
                    }

                    let mut display_args = STUDIO_CMD.to_string();
                    for arg in args {
                        display_args.push(' ');
                        display_args.push_str(arg.to_string_lossy().as_ref());
                    }
                    debug!("Running: {}", display_args);

                    command
                }
            };

            if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
                process::become_command(cmd, args)?;
                Ok(())
            } else {
                Err(Error::ExecCommandNotFound(command))
            }
        }
    }

    fn is_docker_studio(args: &[OsString]) -> bool {
        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return true;
            }
        }

        false
    }

    fn has_docker_group() -> bool {
        let current_user = group::get_current_username().unwrap();
        let docker_members = group::get_members_by_groupname("docker");
        docker_members.map_or(false, |d| d.contains(&current_user))
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI, args: &[OsString]) -> Result<()> {
        // If I have root permissions or if I am executing a docker studio
        // and have the appropriate group - early return, we are done.
        if am_i_root() || (is_docker_studio(args) && has_docker_group()) {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec!["-p".into(),
                                                   "[sudo hab-studio] password for %u: ".into(),
                                                   "-E".into(),];
                args.append(&mut env::args_os().collect());
                process::become_command(sudo_prog, &args)?;
                Ok(())
            }
            None => {
                ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                SUDO_CMD))?;
                ui.warn("Running Habitat Studio requires root or administrator privileges. \
                         Please retry this command as a super user or use a privilege-granting \
                         facility such as sudo.")?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use crate::{command::studio::docker,
                common::ui::UI,
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        fs::find_command,
                        os::process,
                        package::PackageIdent},
                VERSION};
    use ctrlc;
    use std::{ffi::OsString,
              str::FromStr};

    pub async fn start(_ui: &mut UI, args: &[OsString]) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args).await
        } else {
            docker::start_docker_studio(_ui, args)
        }
    }

    pub async fn start_windows_studio(ui: &mut UI, args: &[OsString]) -> Result<()> {
        init()?;
        let version: Vec<&str> = VERSION.split('/').collect();
        let ident =
            PackageIdent::from_str(&format!("{}/{}", super::STUDIO_PACKAGE_IDENT, version[0]))?;
        let pwsh_command = exec::command_from_min_pkg(ui, "pwsh.exe", &ident).await?;
        let studio_command = exec::command_from_min_pkg(ui, "hab-studio.ps1", &ident).await?;

        let mut cmd_args: Vec<OsString> = vec!["-NoProfile",
                                               "-ExecutionPolicy",
                                               "bypass",
                                               "-NoLogo",
                                               "-File"].into_iter()
                                                       .map(Into::into)
                                                       .collect();
        cmd_args.push(studio_command.into());
        cmd_args.extend_from_slice(args);

        if let Some(cmd) = find_command(&pwsh_command) {
            // we need to disable the default ctrlc handler for this process (hab)
            // otherwise when a ctrlc is emitted in the studio, this process will
            // also respond to ctrlc which results in odd stdin/out behavior and
            // forces the user to close the console window entirely
            ctrlc::set_handler(move || {})?;
            process::become_command(cmd, &cmd_args)?;
        } else {
            return Err(Error::ExecCommandNotFound(pwsh_command));
        }
        Ok(())
    }

    fn is_windows_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return false;
            }
        }

        true
    }
}

#[cfg(test)]
mod tests {
    use super::cache_ssl_cert_file;
    use std::fs::File;
    use tempfile::TempDir;

    #[test]
    fn cache_ssl_cert_file_caches_file() -> std::io::Result<()> {
        let cert_name = "ssl-test-cert.pem";
        let cert_cache_dir = TempDir::new()?.into_path();
        let ssl_cert_dir = TempDir::new()?;
        let ssl_cert_filepath = ssl_cert_dir.path().join(cert_name);
        File::create(&ssl_cert_filepath)?;

        cache_ssl_cert_file(&ssl_cert_filepath.to_str().unwrap(), &cert_cache_dir).unwrap();
        assert!(cert_cache_dir.join(cert_name).exists());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_replaces_already_cached() -> std::io::Result<()> {
        let cert_name = "ssl-test-cert.pem";

        let cert_cache_dir = TempDir::new()?.into_path();
        let cached_cert = cert_cache_dir.join(&cert_name);
        File::create(&cached_cert)?;

        let ssl_cert_dir = TempDir::new()?;
        let ssl_cert_filepath = ssl_cert_dir.path().join(&cert_name);
        std::fs::write(&ssl_cert_filepath, "new cert from environment")?;

        cache_ssl_cert_file(&ssl_cert_filepath.to_str().unwrap(), &cert_cache_dir).unwrap();

        let contents = std::fs::read_to_string(&cached_cert)?;

        assert_eq!(contents, "new cert from environment");

        Ok(())
    }
    #[test]
    fn cache_ssl_cert_file_invalid_file() -> std::io::Result<()> {
        let cert_cache_dir = TempDir::new()?.into_path();

        let non_existant_file_name = "i_shouldnt_exist";
        let non_existant_file = TempDir::new()?.path().join(non_existant_file_name);

        assert!(cache_ssl_cert_file(non_existant_file.to_str().unwrap(), &cert_cache_dir).is_err());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_cert_file_is_dir() -> std::io::Result<()> {
        let cert_cache_dir = TempDir::new()?.into_path();
        let ssl_cert_dir = TempDir::new()?.into_path();

        assert!(cache_ssl_cert_file(ssl_cert_dir.to_str().unwrap(), &cert_cache_dir).is_err());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_cert_file_is_cached_file() -> std::io::Result<()> {
        let cached_cert_dir = TempDir::new()?.into_path();
        let cached_cert = cached_cert_dir.join("ssl-cert-file.pem");
        File::create(&cached_cert)?;

        assert!(cache_ssl_cert_file(cached_cert.to_str().unwrap(), &cached_cert_dir).is_err());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_is_empty_string() -> std::io::Result<()> {
        let cached_cert_dir = TempDir::new()?.into_path();

        assert!(cache_ssl_cert_file("", &cached_cert_dir).is_err());

        Ok(())
    }
}
<commit_msg>fixes clippy<commit_after>use std::{env,
          ffi::OsString,
          fs as stdfs,
          path::{Path,
                 PathBuf}};

use crate::{common::ui::{UIWriter,
                         UI},
            hcore::{crypto::CACHE_KEY_PATH_ENV_VAR,
                    env as henv,
                    fs}};

use crate::{config,
            error::{Error,
                    Result},
            BLDR_URL_ENVVAR,
            ORIGIN_ENVVAR};

use habitat_core::AUTH_TOKEN_ENVVAR;
use same_file::is_same_file;

pub const ARTIFACT_PATH_ENVVAR: &str = "ARTIFACT_PATH";
pub const CERT_PATH_ENVVAR: &str = "CERT_PATH";
pub const SSL_CERT_FILE_ENVVAR: &str = "SSL_CERT_FILE";

const STUDIO_PACKAGE_IDENT: &str = "core/hab-studio";

#[derive(Clone, Copy)]
enum Sensitivity {
    PrintValue,
    NoPrintValue,
}

fn set_env_var_from_config(env_var: &str, config_val: Option<String>, sensitive: Sensitivity) {
    if henv::var(env_var).is_err() {
        if let Some(val) = config_val {
            match sensitive {
                Sensitivity::NoPrintValue => {
                    debug!("Setting {}=REDACTED (sensitive) via config file", env_var)
                }
                Sensitivity::PrintValue => debug!("Setting {}={} via config file", env_var, val),
            }
            env::set_var(env_var, val);
        }
    }
}

fn cache_ssl_cert_file(cert_file: &str, cert_cache_dir: &Path) -> Result<()> {
    let cert_path = Path::new(&cert_file);

    let cert_filename = match cert_path.file_name() {
        Some(cert_filename) => cert_filename,
        None => return Err(Error::CacheSslCertError(format!("{:?} is not a file", &cert_file))),
    };
    let cache_file = cert_cache_dir.join(&cert_filename);

    if cache_file.exists() && cert_path.exists() && is_same_file(&cache_file, &cert_path)? {
        return Err(Error::CacheSslCertError("Source and destination certificate are the same \
                                             file"
                                                  .to_string()));
    }

    debug!("Caching SSL_CERT_FILE {:?} => {:?}", cert_file, cache_file);
    stdfs::copy(cert_file, &cache_file)?;

    Ok(())
}

pub async fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
    let config = config::load()?;

    set_env_var_from_config(AUTH_TOKEN_ENVVAR,
                            config.auth_token,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(BLDR_URL_ENVVAR, config.bldr_url, Sensitivity::PrintValue);
    set_env_var_from_config(ORIGIN_ENVVAR, config.origin, Sensitivity::PrintValue);

    if config.ctl_secret.is_some() {
        ui.warn("Your Supervisor CtlGateway secret is not being copied to the Studio \
                 environment because the Studio's Supervisor is local. If you wish to contact a \
                 remote Supervisor from the Studio, please set the HAB_CTL_SECRET variable with \
                 your secret.")?;
    }

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = &*fs::CACHE_KEY_PATH;
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    let ssl_path = match henv::var(CERT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_ssl_path(None::<&str>);
            debug!("Setting {}={}", CERT_PATH_ENVVAR, path.display());
            env::set_var(CERT_PATH_ENVVAR, &path);
            path
        }
    };
    if !ssl_path.is_dir() {
        debug!("Creating ssl_path at: {}", ssl_path.display());
        stdfs::create_dir_all(&ssl_path)?;
    }

    if let Ok(ssl_cert_file) = env::var(SSL_CERT_FILE_ENVVAR) {
        if let Err(err) = cache_ssl_cert_file(&ssl_cert_file, &ssl_path) {
            warn!("Unable to cache SSL_CERT_FILE: {}", err);
        }
    }

    inner::start(ui, args).await
}

#[cfg(target_os = "linux")]
mod inner {
    use crate::{command::studio::docker,
                common::ui::{UIWriter,
                             UI},
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::{am_i_root,
                             find_command},
                        os::process,
                        package::{PackageIdent,
                                  PackageInstall},
                        users::linux as group},
                VERSION};
    use std::{env,
              ffi::OsString,
              path::PathBuf,
              str::FromStr};

    const SUDO_CMD: &str = "sudo";
    const STUDIO_CMD: &str = "hab-studio";
    const STUDIO_CMD_ENVVAR: &str = "HAB_STUDIO_BINARY";

    pub async fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
        rerun_with_sudo_if_needed(ui, &args)?;
        if is_docker_studio(&args) {
            docker::start_docker_studio(ui, args)
        } else {
            let command = match henv::var(STUDIO_CMD_ENVVAR) {
                Ok(command) => PathBuf::from(command),
                Err(_) => {
                    init()?;
                    let version: Vec<&str> = VERSION.split('/').collect();
                    let ident = PackageIdent::from_str(&format!("{}/{}",
                                                                super::STUDIO_PACKAGE_IDENT,
                                                                version[0]))?;
                    let command = exec::command_from_min_pkg(ui, STUDIO_CMD, &ident).await?;
                    // This is a duplicate of the code in `hab pkg exec` and
                    // should be refactored as part of or after:
                    // https://github.com/habitat-sh/habitat/issues/6633
                    // https://github.com/habitat-sh/habitat/issues/6634
                    let pkg_install = PackageInstall::load(&ident, None)?;
                    let cmd_env = pkg_install.environment_for_command()?;
                    for (key, value) in cmd_env.into_iter() {
                        debug!("Setting: {}='{}'", key, value);
                        env::set_var(key, value);
                    }

                    let mut display_args = STUDIO_CMD.to_string();
                    for arg in args {
                        display_args.push(' ');
                        display_args.push_str(arg.to_string_lossy().as_ref());
                    }
                    debug!("Running: {}", display_args);

                    command
                }
            };

            if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
                process::become_command(cmd, args)?;
                Ok(())
            } else {
                Err(Error::ExecCommandNotFound(command))
            }
        }
    }

    fn is_docker_studio(args: &[OsString]) -> bool {
        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return true;
            }
        }

        false
    }

    fn has_docker_group() -> bool {
        let current_user = group::get_current_username().unwrap();
        let docker_members = group::get_members_by_groupname("docker");
        docker_members.map_or(false, |d| d.contains(&current_user))
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI, args: &[OsString]) -> Result<()> {
        // If I have root permissions or if I am executing a docker studio
        // and have the appropriate group - early return, we are done.
        if am_i_root() || (is_docker_studio(args) && has_docker_group()) {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec!["-p".into(),
                                                   "[sudo hab-studio] password for %u: ".into(),
                                                   "-E".into(),];
                args.append(&mut env::args_os().collect());
                process::become_command(sudo_prog, &args)?;
                Ok(())
            }
            None => {
                ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                SUDO_CMD))?;
                ui.warn("Running Habitat Studio requires root or administrator privileges. \
                         Please retry this command as a super user or use a privilege-granting \
                         facility such as sudo.")?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use crate::{command::studio::docker,
                common::ui::UI,
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        fs::find_command,
                        os::process,
                        package::PackageIdent},
                VERSION};
    use std::{ffi::OsString,
              str::FromStr};

    pub async fn start(_ui: &mut UI, args: &[OsString]) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args).await
        } else {
            docker::start_docker_studio(_ui, args)
        }
    }

    pub async fn start_windows_studio(ui: &mut UI, args: &[OsString]) -> Result<()> {
        init()?;
        let version: Vec<&str> = VERSION.split('/').collect();
        let ident =
            PackageIdent::from_str(&format!("{}/{}", super::STUDIO_PACKAGE_IDENT, version[0]))?;
        let pwsh_command = exec::command_from_min_pkg(ui, "pwsh.exe", &ident).await?;
        let studio_command = exec::command_from_min_pkg(ui, "hab-studio.ps1", &ident).await?;

        let mut cmd_args: Vec<OsString> = vec!["-NoProfile",
                                               "-ExecutionPolicy",
                                               "bypass",
                                               "-NoLogo",
                                               "-File"].into_iter()
                                                       .map(Into::into)
                                                       .collect();
        cmd_args.push(studio_command.into());
        cmd_args.extend_from_slice(args);

        if let Some(cmd) = find_command(&pwsh_command) {
            // we need to disable the default ctrlc handler for this process (hab)
            // otherwise when a ctrlc is emitted in the studio, this process will
            // also respond to ctrlc which results in odd stdin/out behavior and
            // forces the user to close the console window entirely
            ctrlc::set_handler(move || {})?;
            process::become_command(cmd, &cmd_args)?;
        } else {
            return Err(Error::ExecCommandNotFound(pwsh_command));
        }
        Ok(())
    }

    fn is_windows_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return false;
            }
        }

        true
    }
}

#[cfg(test)]
mod tests {
    use super::cache_ssl_cert_file;
    use std::fs::File;
    use tempfile::TempDir;

    #[test]
    fn cache_ssl_cert_file_caches_file() -> std::io::Result<()> {
        let cert_name = "ssl-test-cert.pem";
        let cert_cache_dir = TempDir::new()?.into_path();
        let ssl_cert_dir = TempDir::new()?;
        let ssl_cert_filepath = ssl_cert_dir.path().join(cert_name);
        File::create(&ssl_cert_filepath)?;

        cache_ssl_cert_file(&ssl_cert_filepath.to_str().unwrap(), &cert_cache_dir).unwrap();
        assert!(cert_cache_dir.join(cert_name).exists());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_replaces_already_cached() -> std::io::Result<()> {
        let cert_name = "ssl-test-cert.pem";

        let cert_cache_dir = TempDir::new()?.into_path();
        let cached_cert = cert_cache_dir.join(&cert_name);
        File::create(&cached_cert)?;

        let ssl_cert_dir = TempDir::new()?;
        let ssl_cert_filepath = ssl_cert_dir.path().join(&cert_name);
        std::fs::write(&ssl_cert_filepath, "new cert from environment")?;

        cache_ssl_cert_file(&ssl_cert_filepath.to_str().unwrap(), &cert_cache_dir).unwrap();

        let contents = std::fs::read_to_string(&cached_cert)?;

        assert_eq!(contents, "new cert from environment");

        Ok(())
    }
    #[test]
    fn cache_ssl_cert_file_invalid_file() -> std::io::Result<()> {
        let cert_cache_dir = TempDir::new()?.into_path();

        let non_existant_file_name = "i_shouldnt_exist";
        let non_existant_file = TempDir::new()?.path().join(non_existant_file_name);

        assert!(cache_ssl_cert_file(non_existant_file.to_str().unwrap(), &cert_cache_dir).is_err());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_cert_file_is_dir() -> std::io::Result<()> {
        let cert_cache_dir = TempDir::new()?.into_path();
        let ssl_cert_dir = TempDir::new()?.into_path();

        assert!(cache_ssl_cert_file(ssl_cert_dir.to_str().unwrap(), &cert_cache_dir).is_err());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_cert_file_is_cached_file() -> std::io::Result<()> {
        let cached_cert_dir = TempDir::new()?.into_path();
        let cached_cert = cached_cert_dir.join("ssl-cert-file.pem");
        File::create(&cached_cert)?;

        assert!(cache_ssl_cert_file(cached_cert.to_str().unwrap(), &cached_cert_dir).is_err());

        Ok(())
    }

    #[test]
    fn cache_ssl_cert_file_is_empty_string() -> std::io::Result<()> {
        let cached_cert_dir = TempDir::new()?.into_path();

        assert!(cache_ssl_cert_file("", &cached_cert_dir).is_err());

        Ok(())
    }
}
<|endoftext|>