<commit_before>#[macro_use]
extern crate askama;

use askama::Template;

#[derive(Template)]
#[template(path = "base.html")]
struct BaseTemplate<'a> {
    title: &'a str,
}

#[derive(Template)]
#[template(path = "child.html")]
struct ChildTemplate<'a> {
    _parent: BaseTemplate<'a>,
}

#[test]
fn test_use_base_directly() {
    let t = BaseTemplate { title: "Foo" };
    assert_eq!(t.render().unwrap(), "Foo\n\nFoo\nCopyright 2017");
}

#[test]
fn test_simple_extends() {
    let t = ChildTemplate {
        _parent: BaseTemplate { title: "Bar" },
    };
    assert_eq!(
        t.render().unwrap(),
        "Bar\n(Bar) Content goes here\nFoo\nCopyright 2017"
    );
}

pub mod parent {
    use askama::Template;
    #[derive(Template)]
    #[template(path = "base.html")]
    pub struct BaseTemplate<'a> {
        pub title: &'a str,
    }
}

pub mod child {
    use super::parent::*;
    use askama::Template;
    #[derive(Template)]
    #[template(path = "child.html")]
    pub struct ChildTemplate<'a> {
        pub _parent: BaseTemplate<'a>,
    }
}

#[test]
fn test_different_module() {
    let t = child::ChildTemplate {
        _parent: parent::BaseTemplate { title: "a" },
    };
    assert_eq!(
        t.render().unwrap(),
        "a\n(a) Content goes here\nFoo\nCopyright 2017"
    );
}

#[derive(Template)]
#[template(path = "nested-base.html")]
struct NestedBaseTemplate {}

#[derive(Template)]
#[template(path = "nested-child.html")]
struct NestedChildTemplate {
    _parent: NestedBaseTemplate,
}

#[test]
fn test_nested_blocks() {
    let t = NestedChildTemplate {
        _parent: NestedBaseTemplate {},
    };
    assert_eq!(t.render().unwrap(), "\ndurpy\n");
}

#[derive(Template)]
#[template(path = "deep-base.html")]
struct DeepBaseTemplate {
    year: u16,
}

#[derive(Template)]
#[template(path = "deep-mid.html")]
struct DeepMidTemplate {
    _parent: DeepBaseTemplate,
    title: String,
}

#[derive(Template)]
#[template(path = "deep-kid.html")]
struct DeepKidTemplate {
    _parent: DeepMidTemplate,
    item: String,
}

#[test]
fn test_deep() {
    let t = DeepKidTemplate {
        _parent: DeepMidTemplate {
            _parent: DeepBaseTemplate {
                year: 2018,
            },
            title: "Test".into(),
        },
        item: "Foo".into(),
    };

    assert_eq!(t.render().unwrap(), "

  Foo Foo Foo

      nav nav nav

");
    assert_eq!(t._parent.render().unwrap(), "

  Test

      No content found

      nav nav nav

");
    assert_eq!(t._parent._parent.render().unwrap(), "

    nav nav nav
    Copyright 2018

");
}
<commit_msg>Add test for flattened deep inheritance<commit_after>#[macro_use]
extern crate askama;

use askama::Template;

#[derive(Template)]
#[template(path = "base.html")]
struct BaseTemplate<'a> {
    title: &'a str,
}

#[derive(Template)]
#[template(path = "child.html")]
struct ChildTemplate<'a> {
    _parent: BaseTemplate<'a>,
}

#[test]
fn test_use_base_directly() {
    let t = BaseTemplate { title: "Foo" };
    assert_eq!(t.render().unwrap(), "Foo\n\nFoo\nCopyright 2017");
}

#[test]
fn test_simple_extends() {
    let t = ChildTemplate {
        _parent: BaseTemplate { title: "Bar" },
    };
    assert_eq!(
        t.render().unwrap(),
        "Bar\n(Bar) Content goes here\nFoo\nCopyright 2017"
    );
}

pub mod parent {
    use askama::Template;
    #[derive(Template)]
    #[template(path = "base.html")]
    pub struct BaseTemplate<'a> {
        pub title: &'a str,
    }
}

pub mod child {
    use super::parent::*;
    use askama::Template;
    #[derive(Template)]
    #[template(path = "child.html")]
    pub struct ChildTemplate<'a> {
        pub _parent: BaseTemplate<'a>,
    }
}

#[test]
fn test_different_module() {
    let t = child::ChildTemplate {
        _parent: parent::BaseTemplate { title: "a" },
    };
    assert_eq!(
        t.render().unwrap(),
        "a\n(a) Content goes here\nFoo\nCopyright 2017"
    );
}

#[derive(Template)]
#[template(path = "nested-base.html")]
struct NestedBaseTemplate {}

#[derive(Template)]
#[template(path = "nested-child.html")]
struct NestedChildTemplate {
    _parent: NestedBaseTemplate,
}

#[test]
fn test_nested_blocks() {
    let t = NestedChildTemplate {
        _parent: NestedBaseTemplate {},
    };
    assert_eq!(t.render().unwrap(), "\ndurpy\n");
}

#[derive(Template)]
#[template(path = "deep-base.html")]
struct DeepBaseTemplate {
    year: u16,
}

#[derive(Template)]
#[template(path = "deep-mid.html")]
struct DeepMidTemplate {
    _parent: DeepBaseTemplate,
    title: String,
}

#[derive(Template)]
#[template(path = "deep-kid.html")]
struct DeepKidTemplate {
    _parent: DeepMidTemplate,
    item: String,
}

#[test]
fn test_deep() {
    let t = DeepKidTemplate {
        _parent: DeepMidTemplate {
            _parent: DeepBaseTemplate {
                year: 2018,
            },
            title: "Test".into(),
        },
        item: "Foo".into(),
    };

    assert_eq!(t.render().unwrap(), "

  Foo Foo Foo

      nav nav nav

");
    assert_eq!(t._parent.render().unwrap(), "

  Test

      No content found

      nav nav nav

");
    assert_eq!(t._parent._parent.render().unwrap(), "

    nav nav nav
    Copyright 2018

");
}

#[derive(Template)]
#[template(path = "deep-base.html")]
struct FlatDeepBaseTemplate {
    year: u16,
}

#[derive(Template)]
#[template(path = "deep-mid.html")]
struct FlatDeepMidTemplate {
    title: String,
}

#[derive(Template)]
#[template(path = "deep-kid.html")]
struct FlatDeepKidTemplate {
    item: String,
}

#[test]
fn test_flat_deep() {
    let t = FlatDeepKidTemplate {
        item: "Foo".into(),
    };

    assert_eq!(t.render().unwrap(), "

  Foo Foo Foo

      nav nav nav

");

    let t = FlatDeepMidTemplate {
        title: "Test".into(),
    };
    assert_eq!(t.render().unwrap(), "

  Test

      No content found

      nav nav nav

");

    let t = FlatDeepBaseTemplate {
        year: 2018,
    };
    assert_eq!(t.render().unwrap(), "

    nav nav nav
    Copyright 2018

");
}
<|endoftext|>


<commit_before>use redox::get_slice::GetSlice;
use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// Structure which represents a Terminal's command.
/// This command structure contains a name, and the code which run the functionnality associated to this one, with zero, one or several argument(s).
/// # Example
/// ```
/// let my_command = Command {
///     name: "my_command",
///     main: box|args: &Vec<String>| {
///         println!("Say 'hello' to my command! :-D");
///     }
/// }
/// ```
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();

        commands.push(Command {
            name: "cat",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read: {}", path),
                    }
                } else {
                    println!("Failed to open file: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "cd",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            }),
        });

        commands.push(Command {
            name: "echo",
            main: Box::new(|args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            }),
        });

        commands.push(Command {
            name: "else",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "exec",
            main: Box::new(|args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    let mut args_str: Vec<&str> = Vec::new();
                    for arg in args.get_slice(Some(2), None) {
                        args_str.push(arg);
                    }

                    File::exec(arg, &args_str);
                }
            }),
        });

        commands.push(Command {
            name: "exit",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "fi",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "if",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "ls",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(dir) = read_dir(&path) {
                    for entry in dir {
                        println!("{}", entry.path());
                    }
                } else {
                    println!("Failed to open directory: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "pwd",
            main: Box::new(|_: &Vec<String>| {
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        println!("Could not get the path");
                    }
                } else {
                    println!("Could not open the working directory");
                }
            }),
        });

        commands.push(Command {
            name: "read",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "run",
            main: Box::new(|args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            }),
        });

        commands.push(Command {
            name: "sleep",
            main: Box::new(|args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            }),
        });

        commands.push(Command {
            name: "send",
            main: Box::new(|args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "touch",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => if File::create(file_name).is_none() {
                        println!("Failed to create: {}", file_name);
                    },
                    None => println!("No name provided")
                }
            }),
        });

        commands.push(Command {
            name: "url_hex",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        commands.push(Command {
            name: "wget",
            main: Box::new(|args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            }),
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name);

        commands.push(Command {
            name: "help",
            main: Box::new(move |_: &Vec<String>| {
                println!("Commands:{}", command_list);
            }),
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            for variable in self.variables.iter() {
                println!("{}={}", variable.name, variable.value);
            }
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        } else {
                            println!("No right hand side");
                        }
                    } else {
                        println!("No comparison operator");
                    }
                } else {
                    println!("No left hand side");
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            if cmd == "read" {
                for i in 1..args.len() {
                    if let Some(arg_original) = args.get(i) {
                        let arg = arg_original.trim();
                        print!("{}=", arg);
                        if let Some(value_original) = readln!() {
                            let value = value_original.trim();
                            self.set_var(arg, value);
                        }
                    }
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].trim();
                let mut value = cmd[i + 1 .. cmd.len()].trim().to_string();

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                self.set_var(name, &value);
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str){
        if name.is_empty() {
            return;
        }

        if value.is_empty() {
            let mut remove = -1;
            for i in 0..self.variables.len() {
                match self.variables.get(i) {
                    Some(variable) => if variable.name == name {
                        remove = i as isize;
                        break;
                    },
                    None => break,
                }
            }

            if remove >= 0 {
                self.variables.remove(remove as usize);
            }
        } else {
            for variable in self.variables.iter_mut() {
                if variable.name == name {
                    variable.value = value.to_string();
                    return;
                }
            }

            self.variables.push(Variable {
                name: name.to_string(),
                value: value.to_string(),
            });
        }
    }

    /// Method to return the current directory
    /// If the current directory cannot be found, a default string ("?") will be returned
    pub fn get_current_directory(&mut self) -> String {
        // Return the current path
        File::open("")
            .and_then(|file| file.path())
            .unwrap_or("?".to_string())
    }

    /// Run the application
    pub fn main(&mut self) {
        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("user@redox:{}# {}", self.get_current_directory(), command);
            self.on_command(&command);
        }

        loop {
            for mode in self.modes.iter().rev() {
                if mode.value {
                    print!("+ ");
                } else {
                    print!("- ");
                }
            }
            print!("user@redox:{}# ", self.get_current_directory());
            if let Some(command_original) = readln!() {
                let command = command_original.trim();
                if command == "exit" {
                    println!("Exit temporarily blocked (due to using terminal as init)")
                    //break;
                } else if !command.is_empty() {
                    self.on_command(&command);
                }
            } else {
                println!("Failed to read from stdin");
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = Box::new(Application::new());
        application = app.deref_mut();
        app.main();
    }
}
<commit_msg>Command to create a directory added in the Terminal console<commit_after>use redox::get_slice::GetSlice;
use redox::ops::DerefMut;
use redox::string::*;
use redox::vec::Vec;
use redox::boxed::Box;
use redox::fs::*;
use redox::io::*;
use redox::env::*;
use redox::time::Duration;
use redox::to_num::*;

/* Magic Macros { */
static mut application: *mut Application<'static> = 0 as *mut Application;

/// Execute a command
macro_rules! exec {
    ($cmd:expr) => ({
        unsafe {
            (*application).on_command(&$cmd.to_string());
        }
    })
}
/* } Magic Macros */

/// Structure which represents a Terminal's command.
/// This command structure contains a name, and the code which run the functionnality associated to this one, with zero, one or several argument(s).
/// # Example
/// ```
/// let my_command = Command {
///     name: "my_command",
///     main: box|args: &Vec<String>| {
///         println!("Say 'hello' to my command! :-D");
///     }
/// }
/// ```
pub struct Command<'a> {
    pub name: &'a str,
    pub main: Box<Fn(&Vec<String>)>,
}

impl<'a> Command<'a> {
    /// Return the vector of the commands
    // TODO: Use a more efficient collection instead
    pub fn vec() -> Vec<Self> {
        let mut commands: Vec<Self> = Vec::new();

        commands.push(Command {
            name: "cat",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read: {}", path),
                    }
                } else {
                    println!("Failed to open file: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "cd",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(path) => {
                        if !change_cwd(&path) {
                            println!("Bad path: {}", path);
                        }
                    }
                    None => println!("No path given")
                }
            }),
        });

        commands.push(Command {
            name: "echo",
            main: Box::new(|args: &Vec<String>| {
                let echo = args.iter()
                    .skip(1)
                    .fold(String::new(), |string, arg| string + " " + arg);
                println!("{}", echo.trim());
            }),
        });

        commands.push(Command {
            name: "else",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "exec",
            main: Box::new(|args: &Vec<String>| {
                if let Some(arg) = args.get(1) {
                    let mut args_str: Vec<&str> = Vec::new();
                    for arg in args.get_slice(Some(2), None) {
                        args_str.push(arg);
                    }

                    File::exec(arg, &args_str);
                }
            }),
        });

        commands.push(Command {
            name: "exit",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "fi",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "if",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "ls",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(dir) = read_dir(&path) {
                    for entry in dir {
                        println!("{}", entry.path());
                    }
                } else {
                    println!("Failed to open directory: {}", path);
                }
            }),
        });

        commands.push(Command {
            name: "mkdir",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(dir_name) => if DirEntry::create(dir_name).is_none() {
                        println!("Failed to create {}", dir_name);
                    },
                    None => println!("No name provided")
                }
            }),
        });

        commands.push(Command {
            name: "pwd",
            main: Box::new(|_: &Vec<String>| {
                if let Some(file) = File::open("") {
                    if let Some(path) = file.path() {
                        println!("{}", path);
                    } else {
                        println!("Could not get the path");
                    }
                } else {
                    println!("Could not open the working directory");
                }
            }),
        });

        commands.push(Command {
            name: "read",
            main: Box::new(|_: &Vec<String>| {}),
        });

        commands.push(Command {
            name: "run",
            main: Box::new(|args: &Vec<String>| {
                if let Some(path) = args.get(1) {

                    let mut commands = String::new();
                    if let Some(mut file) = File::open(path) {
                        file.read_to_string(&mut commands);
                    }

                    for command in commands.split('\n') {
                        exec!(command);
                    }
                }
            }),
        });

        commands.push(Command {
            name: "sleep",
            main: Box::new(|args: &Vec<String>| {
                let secs = {
                    match args.get(1) {
                        Some(arg) => arg.to_num() as i64,
                        None => 0,
                    }
                };

                let nanos = {
                    match args.get(2) {
                        Some(arg) => arg.to_num() as i32,
                        None => 0,
                    }
                };

                println!("Sleep: {} {}", secs, nanos);
                let remaining = Duration::new(secs, nanos).sleep();
                println!("Remaining: {} {}", remaining.secs, remaining.nanos);
            }),
        });

        commands.push(Command {
            name: "send",
            main: Box::new(|args: &Vec<String>| {
                if args.len() < 3 {
                    println!("Error: incorrect arguments");
                    println!("Usage: send [url] [data]");
                    return;
                }

                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    println!("URL: {:?}", file.path());

                    let string: String = args.iter()
                        .skip(2)
                        .fold(String::new(), |s, arg| s + " " + arg)
                        + "\r\n\r\n";

                    match file.write(string.trim_left().as_bytes()) {
                        Some(size) => println!("Wrote {} bytes", size),
                        None => println!("Failed to write"),
                    }

                    let mut string = String::new();
                    match file.read_to_string(&mut string) {
                        Some(_) => println!("{}", string),
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        // Simple command to create a file, in the current directory
        // The file has got the name given as the first argument of the command
        // If the command have no arguments, the command don't create the file
        commands.push(Command {
            name: "touch",
            main: Box::new(|args: &Vec<String>| {
                match args.get(1) {
                    Some(file_name) => if File::create(file_name).is_none() {
                        println!("Failed to create: {}", file_name);
                    },
                    None => println!("No name provided")
                }
            }),
        });

        commands.push(Command {
            name: "url_hex",
            main: Box::new(|args: &Vec<String>| {
                let path = {
                    match args.get(1) {
                        Some(arg) => arg.clone(),
                        None => String::new(),
                    }
                };

                if let Some(mut file) = File::open(&path) {
                    let mut vec: Vec<u8> = Vec::new();
                    match file.read_to_end(&mut vec) {
                        Some(_) => {
                            let mut line = "HEX:".to_string();
                            for byte in vec.iter() {
                                line = line + " " + &format!("{:X}", *byte);
                            }
                            println!("{}", line);
                        }
                        None => println!("Failed to read"),
                    }
                }
            }),
        });

        commands.push(Command {
            name: "wget",
            main: Box::new(|args: &Vec<String>| {
                if let Some(host) = args.get(1) {
                    if let Some(req) = args.get(2) {
                        if let Some(mut con) = File::open(&("tcp://".to_string() + host)) {
                            con.write(("GET ".to_string() + req + " HTTP/1.1").as_bytes());

                            let mut res = Vec::new();
                            con.read_to_end(&mut res);

                            if let Some(mut file) = File::open(&req) {
                                file.write(&res);
                            }
                        }
                    } else {
                        println!("No request given");
                    }
                } else {
                    println!("No url given");
                }
            }),
        });

        let command_list = commands.iter().fold(String::new(), |l , c| l + " " + c.name);

        commands.push(Command {
            name: "help",
            main: Box::new(move |_: &Vec<String>| {
                println!("Commands:{}", command_list);
            }),
         });

        commands
    }
}

/// A (env) variable
pub struct Variable {
    pub name: String,
    pub value: String,
}

pub struct Mode {
    value: bool,
}

/// An application
pub struct Application<'a> {
    commands: Vec<Command<'a>>,
    variables: Vec<Variable>,
    modes: Vec<Mode>,
}

impl<'a> Application<'a> {
    /// Create a new empty application
    pub fn new() -> Self {
        return Application {
            commands: Command::vec(),
            variables: Vec::new(),
            modes: Vec::new(),
        };
    }

    fn on_command(&mut self, command_string: &str) {
        //Comment
        if command_string.starts_with('#') {
            return;
        }

        //Show variables
        if command_string == "$" {
            for variable in self.variables.iter() {
                println!("{}={}", variable.name, variable.value);
            }
            return;
        }

        //Explode into arguments, replace variables
        let mut args: Vec<String> = Vec::<String>::new();
        for arg in command_string.split(' ') {
            if !arg.is_empty() {
                if arg.starts_with('$') {
                    let name = arg[1 .. arg.len()].to_string();
                    for variable in self.variables.iter() {
                        if variable.name == name {
                            args.push(variable.value.clone());
                            break;
                        }
                    }
                } else {
                    args.push(arg.to_string());
                }
            }
        }

        //Execute commands
        if let Some(cmd) = args.get(0) {
            if cmd == "if" {
                let mut value = false;

                if let Some(left) = args.get(1) {
                    if let Some(cmp) = args.get(2) {
                        if let Some(right) = args.get(3) {
                            if cmp == "==" {
                                value = *left == *right;
                            } else if cmp == "!=" {
                                value = *left != *right;
                            } else if cmp == ">" {
                                value = left.to_num_signed() > right.to_num_signed();
                            } else if cmp == ">=" {
                                value = left.to_num_signed() >= right.to_num_signed();
                            } else if cmp == "<" {
                                value = left.to_num_signed() < right.to_num_signed();
                            } else if cmp == "<=" {
                                value = left.to_num_signed() <= right.to_num_signed();
                            } else {
                                println!("Unknown comparison: {}", cmp);
                            }
                        } else {
                            println!("No right hand side");
                        }
                    } else {
                        println!("No comparison operator");
                    }
                } else {
                    println!("No left hand side");
                }

                self.modes.insert(0, Mode { value: value });
                return;
            }

            if cmd == "else" {
                let mut syntax_error = false;
                match self.modes.get_mut(0) {
                    Some(mode) => mode.value = !mode.value,
                    None => syntax_error = true,
                }
                if syntax_error {
                    println!("Syntax error: else found with no previous if");
                }
                return;
            }

            if cmd == "fi" {
                let mut syntax_error = false;
                if !self.modes.is_empty() {
                    self.modes.remove(0);
                } else {
                    syntax_error = true;
                }
                if syntax_error {
                    println!("Syntax error: fi found with no previous if");
                }
                return;
            }

            for mode in self.modes.iter() {
                if !mode.value {
                    return;
                }
            }

            if cmd == "read" {
                for i in 1..args.len() {
                    if let Some(arg_original) = args.get(i) {
                        let arg = arg_original.trim();
                        print!("{}=", arg);
                        if let Some(value_original) = readln!() {
                            let value = value_original.trim();
                            self.set_var(arg, value);
                        }
                    }
                }
            }

            //Set variables
            if let Some(i) = cmd.find('=') {
                let name = cmd[0 .. i].trim();
                let mut value = cmd[i + 1 .. cmd.len()].trim().to_string();

                for i in 1..args.len() {
                    if let Some(arg) = args.get(i) {
                        value = value + " " + &arg;
                    }
                }

                self.set_var(name, &value);
                return;
            }

            //Commands
            for command in self.commands.iter() {
                if &command.name == cmd {
                    (*command.main)(&args);
                    return;
                }
            }

            println!("Unknown command: '{}'", cmd);
        }
    }

    pub fn set_var(&mut self, name: &str, value: &str){
        if name.is_empty() {
            return;
        }

        if value.is_empty() {
            let mut remove = -1;
            for i in 0..self.variables.len() {
                match self.variables.get(i) {
                    Some(variable) => if variable.name == name {
                        remove = i as isize;
                        break;
                    },
                    None => break,
                }
            }

            if remove >= 0 {
                self.variables.remove(remove as usize);
            }
        } else {
            for variable in self.variables.iter_mut() {
                if variable.name == name {
                    variable.value = value.to_string();
                    return;
                }
            }

            self.variables.push(Variable {
                name: name.to_string(),
                value: value.to_string(),
            });
        }
    }

    /// Method to return the current directory
    /// If the current directory cannot be found, a default string ("?") will be returned
    pub fn get_current_directory(&mut self) -> String {
        // Return the current path
        File::open("")
            .and_then(|file| file.path())
            .unwrap_or("?".to_string())
    }

    /// Run the application
    pub fn main(&mut self) {
        println!("Type help for a command list");
        if let Some(arg) = args().get(1) {
            let command = "run ".to_string() + arg;
            println!("user@redox:{}# {}", self.get_current_directory(), command);
            self.on_command(&command);
        }

        loop {
            for mode in self.modes.iter().rev() {
                if mode.value {
                    print!("+ ");
                } else {
                    print!("- ");
                }
            }
            print!("user@redox:{}# ", self.get_current_directory());
            if let Some(command_original) = readln!() {
                let command = command_original.trim();
                if command == "exit" {
                    println!("Exit temporarily blocked (due to using terminal as init)")
                    //break;
                } else if !command.is_empty() {
                    self.on_command(&command);
                }
            } else {
                println!("Failed to read from stdin");
            }
        }
    }
}

pub fn main() {
    unsafe {
        let mut app = Box::new(Application::new());
        application = app.deref_mut();
        app.main();
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syn::{parse_quote, Ident, Item};

pub(crate) fn create_impl_items(id: &Ident) -> Vec<Item> {
    let bridge_item = Item::Impl(parse_quote! {
        impl UniquePtr<#id> {}
    });
    vec![bridge_item]
}
<commit_msg>impl CxxVector<commit_after>// Copyright 2020 Google LLC
//
// 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
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syn::{parse_quote, Ident, Item};

pub(crate) fn create_impl_items(id: &Ident) -> Vec<Item> {
    vec![
        Item::Impl(parse_quote! {
            impl UniquePtr<#id> {}
        }),
        Item::Impl(parse_quote! {
            impl CxxVector<#id> {}
        }),
    ]
}
<|endoftext|>


<commit_before>extern crate minifb;

use minifb::{Window, Key, WindowOptions};

const WIDTH: usize = 600;
const HEIGHT: usize = 600;
const FRACTAL_DEPTH: u32 = 64;
const GENERATION_INFINITY: f64 = 16.;

fn main() {
  let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];

  let mut window = match Window::new("Fractal - ESC to exit", WIDTH, HEIGHT, WindowOptions::default()) {
    Ok(win) => win,
    Err(err) => {
      println!("Unable to create window {}", err);
      return;
    }
  };

  let range = 2.0;
  let x_min = 0. - range;
  let y_min = 0. - range;

  let x_max = 0. + range;
  let y_max = 0. + range;

  let mut angle: f64 = 0.0;

  while window.is_open() && !window.is_key_down(Key::Escape) {

    for i in 0..buffer.len() {
      let mut real = map((i % WIDTH) as f64, 0., WIDTH as f64, x_min , x_max);
      let mut imag = map((i / HEIGHT) as f64, 0., HEIGHT as f64, y_min, y_max);

      let mut n = 0;

      while n < FRACTAL_DEPTH {
        let re = real.powf(2.) - imag.powf(2.);
        let im = 2. * real * imag;

        real = re + angle.cos();
        imag = im + angle.sin();

        if (real + imag).abs() > GENERATION_INFINITY {
          break;  // Leave when achieve infinity
        }
        n += 1;
      }

      buffer[i] = fill(n);
    }

    angle += 0.1;

    // We unwrap here as we want this code to exit if it fails
    window.update_with_buffer(&buffer).unwrap();
  }
}

fn map (val: f64, start1: f64, stop1: f64, start2: f64, stop2:f64) -> f64 {
  start2 + (stop2 - start2) * ((val - start1) / (stop1 - start1))
}

fn fill (n: u32) -> u32 {
  if FRACTAL_DEPTH == n {
    return 0x00;
  } else {
    return n * 32 % 255;
  }
}
<commit_msg>rustfmt<commit_after>extern crate minifb;

use minifb::{Key, Window, WindowOptions};

const WIDTH: usize = 600;
const HEIGHT: usize = 600;
const FRACTAL_DEPTH: u32 = 64;
const GENERATION_INFINITY: f64 = 16.;

fn main() {
    let mut buffer: Vec<u32> = vec![0; WIDTH * HEIGHT];

    let mut window = match Window::new(
        "Fractal - ESC to exit",
        WIDTH,
        HEIGHT,
        WindowOptions::default(),
    ) {
        Ok(win) => win,
        Err(err) => {
            println!("Unable to create window {}", err);
            return;
        }
    };

    let range = 2.0;
    let x_min = 0. - range;
    let y_min = 0. - range;

    let x_max = 0. + range;
    let y_max = 0. + range;

    let mut angle: f64 = 0.0;

    while window.is_open() && !window.is_key_down(Key::Escape) {
        for i in 0..buffer.len() {
            let mut real = map((i % WIDTH) as f64, 0., WIDTH as f64, x_min, x_max);
            let mut imag = map((i / HEIGHT) as f64, 0., HEIGHT as f64, y_min, y_max);

            let mut n = 0;

            while n < FRACTAL_DEPTH {
                let re = real.powf(2.) - imag.powf(2.);
                let im = 2. * real * imag;

                real = re + angle.cos();
                imag = im + angle.sin();

                if (real + imag).abs() > GENERATION_INFINITY {
                    break; // Leave when achieve infinity
                }
                n += 1;
            }

            buffer[i] = fill(n);
        }

        angle += 0.1;

        // We unwrap here as we want this code to exit if it fails
        window.update_with_buffer(&buffer).unwrap();
    }
}

fn map(val: f64, start1: f64, stop1: f64, start2: f64, stop2: f64) -> f64 {
    start2 + (stop2 - start2) * ((val - start1) / (stop1 - start1))
}

fn fill(n: u32) -> u32 {
    if FRACTAL_DEPTH == n {
        return 0x00;
    } else {
        return n * 32 % 255;
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate pbr;
use rand::Rng;
use pbr::MultiBar;
use std::thread;
use std::time::Duration;

fn main() {
    let mut mb = MultiBar::new();
    mb.println("Your Application Header:");
    mb.println("");

    for i in 1..6 {
        let count = 100 * i;
        let mut pb = mb.create_bar(count);
        pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        pb.show_message = true;
        thread::spawn(move || {
            for _ in 0..count / 20 {
                for _ in 0..20 {
                    pb.message("Waiting  : ");
                    thread::sleep(Duration::from_millis(50));
                    pb.tick();
                }
                for _ in 0..20 {
                    let n = rand::thread_rng().gen_range(0, 100 * i);
                    pb.message("Connected: ");
                    thread::sleep(Duration::from_millis(n));
                    pb.inc();
                }
            }
            for _ in 0..20 {
                pb.message("Cleaning :");
                thread::sleep(Duration::from_millis(100));
                pb.tick();
            }
            pb.finish_print(&format!("{}: Pull complete", rand_string()));
        });
    }

    mb.println("");
    mb.println("Text lines separate between two sections: ");
    mb.println("");

    for i in 1..4 {
        let count = 100 * i;
        let mut pb = mb.create_bar(count);
        thread::spawn(move || {
            for _ in 0..count {
                pb.inc();
                let n = rand::thread_rng().gen_range(0, 100 * i);
                thread::sleep(Duration::from_millis(n));
            }
            pb.finish();
        });
    }

    mb.listen();

    println!("\nall bars done!\n");
}

fn rand_string() -> String {
    let mut v = Vec::new();
    while v.len() < 12 {
        let b = rand::random::<u8>();
        // [0-9a-f]
        if b > 47 && b < 58 || b > 96 && b < 103 {
            v.push(b);
        }
    }
    std::str::from_utf8(&v).unwrap().to_string()
}
<commit_msg>examples: make multi shorter<commit_after>extern crate rand;
extern crate pbr;
use rand::Rng;
use pbr::MultiBar;
use std::thread;
use std::time::Duration;

fn main() {
    let mut mb = MultiBar::new();
    mb.println("Your Application Header:");
    mb.println("");

    for i in 1..6 {
        let count = 50 * i;
        let mut pb = mb.create_bar(count);
        pb.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        pb.show_message = true;
        thread::spawn(move || {
            for _ in 0..count / 20 {
                for _ in 0..20 {
                    pb.message("Waiting  : ");
                    thread::sleep(Duration::from_millis(50));
                    pb.tick();
                }
                for _ in 0..20 {
                    let n = rand::thread_rng().gen_range(0, 100);
                    pb.message("Connected: ");
                    thread::sleep(Duration::from_millis(n));
                    pb.inc();
                }
            }
            for _ in 0..20 {
                pb.message("Cleaning :");
                thread::sleep(Duration::from_millis(100));
                pb.tick();
            }
            pb.finish_print(&format!("{}: Pull complete", rand_string()));
        });
    }

    mb.println("");
    mb.println("Text lines separate between two sections: ");
    mb.println("");

    for i in 1..4 {
        let count = 100 * i;
        let mut pb = mb.create_bar(count);
        thread::spawn(move || {
            for _ in 0..count {
                pb.inc();
                let n = rand::thread_rng().gen_range(0, 100);
                thread::sleep(Duration::from_millis(n));
            }
            pb.finish();
        });
    }

    mb.listen();

    println!("\nall bars done!\n");
}

fn rand_string() -> String {
    let mut v = Vec::new();
    while v.len() < 12 {
        let b = rand::random::<u8>();
        // [0-9a-f]
        if b > 47 && b < 58 || b > 96 && b < 103 {
            v.push(b);
        }
    }
    std::str::from_utf8(&v).unwrap().to_string()
}
<|endoftext|>


<commit_before>extern crate minifb;

use minifb::*;

const WIDTH: usize = 640;
const HEIGHT: usize = 360;

fn main() {
    let mut noise;
    let mut carry;
    let mut seed = 0xbeefu32;

    let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];

    let mut window = Window::new("Noise Test - Press ESC to exit",
                                 WIDTH,
                                 HEIGHT,
                                 Scale::X2)
                         .unwrap();

    while window.is_open() && !window.is_key_down(Key::Escape) {
        for i in buffer.iter_mut() {
            noise = seed;
            noise >>= 3;
            noise ^= seed;
            carry = noise & 1;
            noise >>= 1;
            seed >>= 1;
            seed |= carry << 30;
            noise &= 0xFF;
            *i = (noise << 16) | (noise << 8) | noise;
        }

        window.get_keys_pressed(KeyRepeat::No).map(|keys| {
            for t in keys.iter() {
                match *t {
                    Key::W => println!("holding w!"),
                    Key::T => println!("holding t!"),
                    _ => (),
                }
            }
        });

        window.update(&buffer);
    }
}
<commit_msg>Proper error handling if window create fail<commit_after>extern crate minifb;

use minifb::*;

const WIDTH: usize = 640;
const HEIGHT: usize = 360;

fn main() {
    let mut noise;
    let mut carry;
    let mut seed = 0xbeefu32;

    let mut buffer: [u32; WIDTH * HEIGHT] = [0; WIDTH * HEIGHT];

    let mut window = match Window::new("Noise Test - Press ESC to exit", WIDTH, HEIGHT, Scale::X2) {
        Ok(win) => win,
        Err(err) => {
            println!("Unable to create window {}", err);
            return;
        }
    };

    while window.is_open() && !window.is_key_down(Key::Escape) {
        for i in buffer.iter_mut() {
            noise = seed;
            noise >>= 3;
            noise ^= seed;
            carry = noise & 1;
            noise >>= 1;
            seed >>= 1;
            seed |= carry << 30;
            noise &= 0xFF;
            *i = (noise << 16) | (noise << 8) | noise;
        }

        window.get_keys_pressed(KeyRepeat::No).map(|keys| {
            for t in keys.iter() {
                match *t {
                    Key::W => println!("holding w!"),
                    Key::T => println!("holding t!"),
                    _ => (),
                }
            }
        });

        window.update(&buffer);
    }
}
<|endoftext|>


<commit_before>use std::{
    collections::HashMap,
    ffi::OsString,
    fs, io,
    path::{Path, PathBuf},
};

use oci_spec::image::{Descriptor, DescriptorBuilder, MediaType};

use archive_utils::{flate2, tar, zstd};
use common::{
    chrono::Utc,
    eyre::{bail, eyre, Result},
    itertools::Itertools,
    tracing,
};
use hash_utils::{sha2::Digest, sha2::Sha256};

use crate::{blob_writer::BlobWriter, storage::digest_to_parts};

/// A change in a path between two snapshots
///
/// This enum represents the [Change Types](https://github.com/opencontainers/image-spec/blob/main/layer.md#change-types)
/// described in the OCI spec.
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd)]
pub enum Change {
    Added(String),
    Modified(String),
    Removed(String),
}

/// The set of changes between two snapshots
///
/// Represents the set of changes between two filesystem snapshots as described in
/// [OCI Image Layer Filesystem Changeset](https://github.com/opencontainers/image-spec/blob/main/layer.md)
pub struct ChangeSet {
    /// The source directory, on the local filesystem, for the changes
    source_dir: PathBuf,

    /// The destination directory, within the image's root filesystem, for the changes
    dest_dir: PathBuf,

    /// The change items
    pub(crate) items: Vec<Change>,
}

impl ChangeSet {
    /// Create a new set of snapshot changes
    pub fn new<P: AsRef<Path>>(source_dir: P, dest_dir: P, items: Vec<Change>) -> Self {
        let source_dir = source_dir.as_ref().to_path_buf();

        // Parths in tar archive must be relative so stri any leading slash
        let dest_dir = dest_dir.as_ref().to_path_buf();
        let dest_dir = match dest_dir.strip_prefix("/") {
            Ok(dir) => dir.to_owned(),
            Err(_) => dest_dir,
        };

        Self {
            source_dir,
            dest_dir,
            items,
        }
    }

    /// Creates an OCI layer for the set of changes
    ///
    /// This implements the [Representing Changes](https://github.com/opencontainers/image-spec/blob/main/layer.md#representing-changes)
    /// section of the OCI image spec:
    ///
    /// - `Added` and `Modified` paths are added to the archive.
    /// - `Removed` paths are represented as "whiteout" files.
    ///
    /// Note that two SHA256 hashes are calculated, one for the `DiffID` of a changeset (calculated in this function
    /// and used in the image config file) and one for the digest which (calculated by the [`BlobWriter`] and used in the image manifest).
    /// A useful diagram showing how these are calculated and used is available
    /// [here](https://github.com/google/go-containerregistry/blob/main/pkg/v1/remote/README.md#anatomy-of-an-image-upload).
    ///
    /// # Arguments
    ///
    /// - `layout_dir`: the image directory to write the layer to (to the `blob/sha256` subdirectory)
    pub fn write_layer<P: AsRef<Path>>(
        self,
        media_type: &MediaType,
        layout_dir: P,
    ) -> Result<(String, Descriptor)> {
        if self.items.is_empty() {
            return Ok((
                "<empty>".to_string(),
                DescriptorBuilder::default()
                    .media_type(media_type.clone())
                    .digest("<none>")
                    .size(0)
                    .build()?,
            ));
        }

        tracing::info!(
            "Writing image layer from changeset for `{}`",
            self.source_dir.display()
        );

        let mut diffid_hash = Sha256::new();
        let mut blob_writer = BlobWriter::new()?;

        let changes = self.items.len();
        let mut additions: Vec<String> = Vec::new();
        let mut modifications: Vec<String> = Vec::new();
        let mut deletions: Vec<String> = Vec::new();

        {
            enum LayerEncoder<'lt> {
                Plain(&'lt mut BlobWriter),
                Gzip(flate2::write::GzEncoder<&'lt mut BlobWriter>),
                Zstd(zstd::stream::AutoFinishEncoder<'lt, &'lt mut BlobWriter>),
            }

            struct LayerWriter<'lt> {
                diffid_hash: &'lt mut Sha256,
                layer_encoder: LayerEncoder<'lt>,
            }

            impl<'lt> io::Write for LayerWriter<'lt> {
                fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                    self.diffid_hash.update(buf);
                    match &mut self.layer_encoder {
                        LayerEncoder::Plain(encoder) => encoder.write_all(buf)?,
                        LayerEncoder::Gzip(encoder) => encoder.write_all(buf)?,
                        LayerEncoder::Zstd(encoder) => encoder.write_all(buf)?,
                    }
                    Ok(buf.len())
                }

                fn flush(&mut self) -> io::Result<()> {
                    Ok(())
                }
            }

            let layer_encoder = match media_type {
                MediaType::ImageLayer => LayerEncoder::Plain(&mut blob_writer),
                MediaType::ImageLayerGzip => LayerEncoder::Gzip(flate2::write::GzEncoder::new(
                    &mut blob_writer,
                    flate2::Compression::new(4),
                )),
                MediaType::ImageLayerZstd => LayerEncoder::Zstd(
                    zstd::stream::Encoder::new(&mut blob_writer, 4)?.auto_finish(),
                ),
                _ => bail!("Unhandled media type for layer: {}", media_type.to_string()),
            };

            let mut layer_writer = LayerWriter {
                diffid_hash: &mut diffid_hash,
                layer_encoder,
            };

            let mut archive = tar::Builder::new(&mut layer_writer);

            // Add an entry for the `dest_dir` (and any of its parent) so that ownership (and other
            // metadata) of `source_dir` is maintained. If not done then there are issues with non-root
            // users writing to the `workspace` and `layers` directories and  their subdirectories.
            let mut path = PathBuf::new();
            for part in self.dest_dir.components() {
                path = path.join(part);
                archive.append_path_with_name(&self.source_dir, &path)?;
            }

            // Add each change in sorted order so that the digest of the generated archive
            // does not change for a given change set
            for change in self.items.into_iter().sorted() {
                match change {
                    Change::Added(ref path) | Change::Modified(ref path) => {
                        let source_path = self.source_dir.join(path);
                        let dest_path = self.dest_dir.join(path);

                        let result = if source_path.is_symlink() {
                            match fs::read_link(&source_path).and_then(|target| {
                                fs::metadata(&source_path).map(|metadata| (target, metadata))
                            }) {
                                Ok((target, metadata)) => {
                                    #[cfg(target_family = "unix")]
                                    let (uid, gid) = {
                                        use std::os::unix::prelude::MetadataExt;
                                        (metadata.uid(), metadata.gid())
                                    };

                                    #[cfg(not(target_family = "unix"))]
                                    let (uid, gid) = (1000u32, 1000u32);

                                    let mut header = tar::Header::new_gnu();
                                    header.set_uid(uid.into());
                                    header.set_gid(gid.into());
                                    header.set_entry_type(tar::EntryType::Symlink);
                                    header.set_size(0);
                                    archive.append_link(&mut header, dest_path, target)
                                }
                                Err(error) => Err(error),
                            }
                        } else {
                            archive.append_path_with_name(source_path, dest_path)
                        };

                        if let Err(error) = result {
                            tracing::debug!(
                                "While appending item for added or modified path `{}`: {}",
                                path,
                                error
                            )
                        } else {
                            match change {
                                Change::Added(..) => additions.push(path.to_string()),
                                Change::Modified(..) => modifications.push(path.to_string()),
                                _ => unreachable!(),
                            }
                        }
                    }
                    Change::Removed(path) => {
                        let path_buf = PathBuf::from(&path);
                        let basename = path_buf
                            .file_name()
                            .ok_or_else(|| eyre!("Path has no file name"))?;
                        let mut whiteout = OsString::from(".wh.".to_string());
                        whiteout.push(basename);
                        let path_buf = match path_buf.parent() {
                            Some(parent) => parent.join(whiteout),
                            None => PathBuf::from(whiteout),
                        };
                        let path_buf = self.dest_dir.join(path_buf);

                        let mut header = tar::Header::new_gnu();
                        header.set_path(path_buf)?;
                        header.set_size(0);
                        header.set_cksum();
                        let data: &[u8] = &[];

                        if let Err(error) = archive.append(&header, data) {
                            tracing::debug!(
                                "While appending item for deleted path `{}`: {}",
                                path,
                                error
                            )
                        } else {
                            deletions.push(path)
                        }
                    }
                };
            }
        }

        let diff_id = format!("sha256:{:x}", diffid_hash.finalize());

        let mut annotations: HashMap<String, String> = [
            ("io.stencila.version", env!("CARGO_PKG_VERSION").to_string()),
            ("io.stencila.layer.created", Utc::now().to_rfc3339()),
            (
                "io.stencila.layer.directory",
                self.dest_dir.to_string_lossy().to_string(),
            ),
            ("io.stencila.layer.changes", changes.to_string()),
        ]
        .map(|(name, value)| (name.to_string(), value))
        .into();

        fn first_100(vec: Vec<String>) -> String {
            vec[..(std::cmp::min(vec.len(), 100))].join(":")
        }
        if !additions.is_empty() {
            annotations.insert(
                "io.stencila.layer.additions".to_string(),
                first_100(additions),
            );
        }
        if !modifications.is_empty() {
            annotations.insert(
                "io.stencila.layer.modifications".to_string(),
                first_100(modifications),
            );
        }
        if !deletions.is_empty() {
            annotations.insert(
                "io.stencila.layer.deletions".to_string(),
                first_100(deletions),
            );
        }

        let descriptor = blob_writer.finish(
            media_type.to_owned(),
            Some(annotations),
            Some(layout_dir.as_ref()),
        )?;

        Ok((diff_id, descriptor))
    }

    /// Get the path of a layer blob within an image directory
    ///
    /// # Arguments
    ///
    /// - `image_dir`: the image directory
    /// - `digest`: the digest of the layer (with or without the "sha256:" prefix)
    fn layer_path<P: AsRef<Path>>(image_dir: P, digest: &str) -> PathBuf {
        let (algo, hash) = digest_to_parts(digest);
        image_dir.as_ref().join("blobs").join(algo).join(hash)
    }

    /// Read a layer blob (a compressed tar archive) from an image directory
    ///
    /// At this stage, mainly just used for testing.
    ///
    /// # Arguments
    ///
    /// - `image_dir`: the image directory
    /// - `digest`: the digest of the layer (with or without the "sha256:" prefix)
    pub fn read_layer<P: AsRef<Path>>(
        image_dir: P,
        digest: &str,
    ) -> Result<tar::Archive<flate2::read::GzDecoder<std::fs::File>>> {
        let path = Self::layer_path(image_dir, digest);
        let file = fs::File::open(&path)?;
        let decoder = flate2::read::GzDecoder::new(file);
        let archive = tar::Archive::new(decoder);
        Ok(archive)
    }
}

#[cfg(test)]
mod tests {
    use hash_utils::file_sha256_hex;
    use test_utils::{common::tempfile::tempdir, skip_ci_os};

    use crate::{snapshot::Snapshot, storage::digest_to_parts};

    use super::*;

    /// Test that the descriptor for a layer is accurate (SHA256 and size are same as
    /// when independently calculated)
    #[test]
    fn changes_layer() -> Result<()> {
        if skip_ci_os(
            "macos",
            "Currently failing with Error: No such file or directory (os error 2)",
        ) {
            return Ok(());
        }

        let source_dir = tempdir()?;
        let layout_dir = tempdir()?;

        let snap = Snapshot::new(&source_dir, "workspace");

        fs::write(&source_dir.path().join("some-file.txt"), "Hello")?;

        // Create a layer archive, diffid and descriptor
        let changes = snap.changes();
        let (diff_id, descriptor) = changes.write_layer(&MediaType::ImageLayerGzip, &layout_dir)?;

        assert_eq!(diff_id.len(), 7 + 64);
        assert!(diff_id.starts_with("sha256:"));

        // Test that size and digest in the descriptor is as for the file
        let (algo, hash) = digest_to_parts(descriptor.digest());
        let archive = layout_dir.path().join("blobs").join(algo).join(hash);

        let size = fs::metadata(&archive)?.len() as i64;
        assert_eq!(descriptor.size(), size);

        let digest = format!("sha256:{}", file_sha256_hex(&archive)?);
        assert_eq!(descriptor.digest(), &digest);

        Ok(())
    }
}
<commit_msg>If change_set includes a new folder, the generated sha256 digest will be different, even though the folder name is the same(but the origin super path is different)<commit_after>use std::{
    collections::HashMap,
    ffi::OsString,
    fs, io,
    path::{Path, PathBuf},
};

use oci_spec::image::{Descriptor, DescriptorBuilder, MediaType};

use archive_utils::{
    flate2,
    tar::{self, HeaderMode},
    zstd,
};
use common::{
    chrono::Utc,
    eyre::{bail, eyre, Result},
    itertools::Itertools,
    tracing,
};
use hash_utils::{sha2::Digest, sha2::Sha256};

use crate::{blob_writer::BlobWriter, storage::digest_to_parts};

/// A change in a path between two snapshots
///
/// This enum represents the [Change Types](https://github.com/opencontainers/image-spec/blob/main/layer.md#change-types)
/// described in the OCI spec.
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd)]
pub enum Change {
    Added(String),
    Modified(String),
    Removed(String),
}

/// The set of changes between two snapshots
///
/// Represents the set of changes between two filesystem snapshots as described in
/// [OCI Image Layer Filesystem Changeset](https://github.com/opencontainers/image-spec/blob/main/layer.md)
pub struct ChangeSet {
    /// The source directory, on the local filesystem, for the changes
    source_dir: PathBuf,

    /// The destination directory, within the image's root filesystem, for the changes
    dest_dir: PathBuf,

    /// The change items
    pub(crate) items: Vec<Change>,
}

impl ChangeSet {
    /// Create a new set of snapshot changes
    pub fn new<P: AsRef<Path>>(source_dir: P, dest_dir: P, items: Vec<Change>) -> Self {
        let source_dir = source_dir.as_ref().to_path_buf();

        // Parths in tar archive must be relative so stri any leading slash
        let dest_dir = dest_dir.as_ref().to_path_buf();
        let dest_dir = match dest_dir.strip_prefix("/") {
            Ok(dir) => dir.to_owned(),
            Err(_) => dest_dir,
        };

        Self {
            source_dir,
            dest_dir,
            items,
        }
    }

    /// Creates an OCI layer for the set of changes
    ///
    /// This implements the [Representing Changes](https://github.com/opencontainers/image-spec/blob/main/layer.md#representing-changes)
    /// section of the OCI image spec:
    ///
    /// - `Added` and `Modified` paths are added to the archive.
    /// - `Removed` paths are represented as "whiteout" files.
    ///
    /// Note that two SHA256 hashes are calculated, one for the `DiffID` of a changeset (calculated in this function
    /// and used in the image config file) and one for the digest which (calculated by the [`BlobWriter`] and used in the image manifest).
    /// A useful diagram showing how these are calculated and used is available
    /// [here](https://github.com/google/go-containerregistry/blob/main/pkg/v1/remote/README.md#anatomy-of-an-image-upload).
    ///
    /// # Arguments
    ///
    /// - `layout_dir`: the image directory to write the layer to (to the `blob/sha256` subdirectory)
    pub fn write_layer<P: AsRef<Path>>(
        self,
        media_type: &MediaType,
        layout_dir: P,
    ) -> Result<(String, Descriptor)> {
        if self.items.is_empty() {
            return Ok((
                "<empty>".to_string(),
                DescriptorBuilder::default()
                    .media_type(media_type.clone())
                    .digest("<none>")
                    .size(0)
                    .build()?,
            ));
        }

        tracing::info!(
            "Writing image layer from changeset for `{}`",
            self.source_dir.display()
        );

        let mut diffid_hash = Sha256::new();
        let mut blob_writer = BlobWriter::new()?;

        let changes = self.items.len();
        let mut additions: Vec<String> = Vec::new();
        let mut modifications: Vec<String> = Vec::new();
        let mut deletions: Vec<String> = Vec::new();

        {
            enum LayerEncoder<'lt> {
                Plain(&'lt mut BlobWriter),
                Gzip(flate2::write::GzEncoder<&'lt mut BlobWriter>),
                Zstd(zstd::stream::AutoFinishEncoder<'lt, &'lt mut BlobWriter>),
            }

            struct LayerWriter<'lt> {
                diffid_hash: &'lt mut Sha256,
                layer_encoder: LayerEncoder<'lt>,
            }

            impl<'lt> io::Write for LayerWriter<'lt> {
                fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
                    self.diffid_hash.update(buf);
                    match &mut self.layer_encoder {
                        LayerEncoder::Plain(encoder) => encoder.write_all(buf)?,
                        LayerEncoder::Gzip(encoder) => encoder.write_all(buf)?,
                        LayerEncoder::Zstd(encoder) => encoder.write_all(buf)?,
                    }
                    Ok(buf.len())
                }

                fn flush(&mut self) -> io::Result<()> {
                    Ok(())
                }
            }

            let layer_encoder = match media_type {
                MediaType::ImageLayer => LayerEncoder::Plain(&mut blob_writer),
                MediaType::ImageLayerGzip => LayerEncoder::Gzip(flate2::write::GzEncoder::new(
                    &mut blob_writer,
                    flate2::Compression::new(4),
                )),
                MediaType::ImageLayerZstd => LayerEncoder::Zstd(
                    zstd::stream::Encoder::new(&mut blob_writer, 4)?.auto_finish(),
                ),
                _ => bail!("Unhandled media type for layer: {}", media_type.to_string()),
            };

            let mut layer_writer = LayerWriter {
                diffid_hash: &mut diffid_hash,
                layer_encoder,
            };

            let mut archive = tar::Builder::new(&mut layer_writer);
            archive.mode(HeaderMode::Deterministic);
            // Add an entry for the `dest_dir` (and any of its parent) so that ownership (and other
            // metadata) of `source_dir` is maintained. If not done then there are issues with non-root
            // users writing to the `workspace` and `layers` directories and  their subdirectories.
            let mut path = PathBuf::new();
            for part in self.dest_dir.components() {
                path = path.join(part);
                archive.append_path_with_name(&self.source_dir, &path)?;
            }

            // Add each change in sorted order so that the digest of the generated archive
            // does not change for a given change set
            for change in self.items.into_iter().sorted() {
                match change {
                    Change::Added(ref path) | Change::Modified(ref path) => {
                        let source_path = self.source_dir.join(path);
                        let dest_path = self.dest_dir.join(path);

                        let result = if source_path.is_symlink() {
                            match fs::read_link(&source_path).and_then(|target| {
                                fs::metadata(&source_path).map(|metadata| (target, metadata))
                            }) {
                                Ok((target, metadata)) => {
                                    #[cfg(target_family = "unix")]
                                    let (uid, gid) = {
                                        use std::os::unix::prelude::MetadataExt;
                                        (metadata.uid(), metadata.gid())
                                    };

                                    #[cfg(not(target_family = "unix"))]
                                    let (uid, gid) = (1000u32, 1000u32);

                                    let mut header = tar::Header::new_gnu();
                                    header.set_uid(uid.into());
                                    header.set_gid(gid.into());
                                    header.set_entry_type(tar::EntryType::Symlink);
                                    header.set_size(0);
                                    archive.append_link(&mut header, dest_path, target)
                                }
                                Err(error) => Err(error),
                            }
                        } else {
                            archive.append_path_with_name(source_path, dest_path)
                        };

                        if let Err(error) = result {
                            tracing::debug!(
                                "While appending item for added or modified path `{}`: {}",
                                path,
                                error
                            )
                        } else {
                            match change {
                                Change::Added(..) => additions.push(path.to_string()),
                                Change::Modified(..) => modifications.push(path.to_string()),
                                _ => unreachable!(),
                            }
                        }
                    }
                    Change::Removed(path) => {
                        let path_buf = PathBuf::from(&path);
                        let basename = path_buf
                            .file_name()
                            .ok_or_else(|| eyre!("Path has no file name"))?;
                        let mut whiteout = OsString::from(".wh.".to_string());
                        whiteout.push(basename);
                        let path_buf = match path_buf.parent() {
                            Some(parent) => parent.join(whiteout),
                            None => PathBuf::from(whiteout),
                        };
                        let path_buf = self.dest_dir.join(path_buf);

                        let mut header = tar::Header::new_gnu();
                        header.set_path(path_buf)?;
                        header.set_size(0);
                        header.set_cksum();
                        let data: &[u8] = &[];

                        if let Err(error) = archive.append(&header, data) {
                            tracing::debug!(
                                "While appending item for deleted path `{}`: {}",
                                path,
                                error
                            )
                        } else {
                            deletions.push(path)
                        }
                    }
                };
            }
        }

        let diff_id = format!("sha256:{:x}", diffid_hash.finalize());

        let mut annotations: HashMap<String, String> = [
            ("io.stencila.version", env!("CARGO_PKG_VERSION").to_string()),
            ("io.stencila.layer.created", Utc::now().to_rfc3339()),
            (
                "io.stencila.layer.directory",
                self.dest_dir.to_string_lossy().to_string(),
            ),
            ("io.stencila.layer.changes", changes.to_string()),
        ]
        .map(|(name, value)| (name.to_string(), value))
        .into();

        fn first_100(vec: Vec<String>) -> String {
            vec[..(std::cmp::min(vec.len(), 100))].join(":")
        }
        if !additions.is_empty() {
            annotations.insert(
                "io.stencila.layer.additions".to_string(),
                first_100(additions),
            );
        }
        if !modifications.is_empty() {
            annotations.insert(
                "io.stencila.layer.modifications".to_string(),
                first_100(modifications),
            );
        }
        if !deletions.is_empty() {
            annotations.insert(
                "io.stencila.layer.deletions".to_string(),
                first_100(deletions),
            );
        }

        let descriptor = blob_writer.finish(
            media_type.to_owned(),
            Some(annotations),
            Some(layout_dir.as_ref()),
        )?;

        Ok((diff_id, descriptor))
    }

    /// Get the path of a layer blob within an image directory
    ///
    /// # Arguments
    ///
    /// - `image_dir`: the image directory
    /// - `digest`: the digest of the layer (with or without the "sha256:" prefix)
    fn layer_path<P: AsRef<Path>>(image_dir: P, digest: &str) -> PathBuf {
        let (algo, hash) = digest_to_parts(digest);
        image_dir.as_ref().join("blobs").join(algo).join(hash)
    }

    /// Read a layer blob (a compressed tar archive) from an image directory
    ///
    /// At this stage, mainly just used for testing.
    ///
    /// # Arguments
    ///
    /// - `image_dir`: the image directory
    /// - `digest`: the digest of the layer (with or without the "sha256:" prefix)
    pub fn read_layer<P: AsRef<Path>>(
        image_dir: P,
        digest: &str,
    ) -> Result<tar::Archive<flate2::read::GzDecoder<std::fs::File>>> {
        let path = Self::layer_path(image_dir, digest);
        let file = fs::File::open(&path)?;
        let decoder = flate2::read::GzDecoder::new(file);
        let archive = tar::Archive::new(decoder);
        Ok(archive)
    }
}

#[cfg(test)]
mod tests {
    use hash_utils::file_sha256_hex;
    use test_utils::{common::tempfile::tempdir, skip_ci_os};

    use crate::{snapshot::Snapshot, storage::digest_to_parts};

    use super::*;

    /// Test that the descriptor for a layer is accurate (SHA256 and size are same as
    /// when independently calculated)
    #[test]
    fn changes_layer() -> Result<()> {
        if skip_ci_os(
            "macos",
            "Currently failing with Error: No such file or directory (os error 2)",
        ) {
            return Ok(());
        }

        let source_dir = tempdir()?;
        let layout_dir = tempdir()?;

        let snap = Snapshot::new(&source_dir, "workspace");

        fs::write(&source_dir.path().join("some-file.txt"), "Hello")?;

        // Create a layer archive, diffid and descriptor
        let changes = snap.changes();
        let (diff_id, descriptor) = changes.write_layer(&MediaType::ImageLayerGzip, &layout_dir)?;

        assert_eq!(diff_id.len(), 7 + 64);
        assert!(diff_id.starts_with("sha256:"));

        // Test that size and digest in the descriptor is as for the file
        let (algo, hash) = digest_to_parts(descriptor.digest());
        let archive = layout_dir.path().join("blobs").join(algo).join(hash);

        let size = fs::metadata(&archive)?.len() as i64;
        assert_eq!(descriptor.size(), size);

        let digest = format!("sha256:{}", file_sha256_hex(&archive)?);
        assert_eq!(descriptor.digest(), &digest);

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