<commit_before>#[deriving(PartialEq, Show)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

pub fn tokenize(input: &str) -> Tokenizer {
    Tokenizer {text: String::from_str(input)}
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub struct Tokenizer {
    text: String
}

impl Iterator<Token> for Tokenizer {
    fn next(&mut self) -> Option<Token> {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return None
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + self.read_until_whitespace().as_slice();
                number = number.trim_left_chars('+').to_string();
                match number.parse::<f64>() {
                    Some(parsed_num) => Some(Token::Number(parsed_num)),
                    None => panic!("Could not parse number: {}", number),
                }
            }
            c => {
                let word = c.to_string() + self.read_until_whitespace().as_slice();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokenizer {
    fn pop_char(&mut self) -> Option<char> {
        if self.text.is_empty() {
            None
        } else {
            Some(self.text.remove(0))
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return "".to_string()
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + self.read_until_whitespace().as_slice(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<commit_msg>Stop using deprecated method<commit_after>#[deriving(PartialEq, Show)]
pub enum Token {
    Comma,
    Number(f64),
    ParenClose,
    ParenOpen,
    Word(String),
}

pub fn tokenize(input: &str) -> Tokenizer {
    Tokenizer {text: String::from_str(input)}
}

fn is_whitespace(c: char) -> bool {
    match c {
        '\n' | '\r' | '\t' | ' ' => true,
        _                        => false,
    }
}

fn is_numberlike(c: char) -> bool {
    match c {
        c if c.is_numeric() => true,
        '.' | '-' | '+'     => true,
        _                   => false,
    }
}

pub struct Tokenizer {
    text: String
}

impl Iterator<Token> for Tokenizer {
    fn next(&mut self) -> Option<Token> {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return None
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' => None,
            '(' => Some(Token::ParenOpen),
            ')' => Some(Token::ParenClose),
            ',' => Some(Token::Comma),
            c if is_whitespace(c) => self.next(),
            c if is_numberlike(c) => {
                let mut number = c.to_string() + self.read_until_whitespace().as_slice();
                number = number.trim_left_matches('+').to_string();
                match number.parse::<f64>() {
                    Some(parsed_num) => Some(Token::Number(parsed_num)),
                    None => panic!("Could not parse number: {}", number),
                }
            }
            c => {
                let word = c.to_string() + self.read_until_whitespace().as_slice();
                Some(Token::Word(word))
            }
        }
    }
}

impl Tokenizer {
    fn pop_char(&mut self) -> Option<char> {
        if self.text.is_empty() {
            None
        } else {
            Some(self.text.remove(0))
        }
    }

    fn read_until_whitespace(&mut self) -> String {
        let popped_char = self.pop_char();
        if popped_char.is_none() {
            return "".to_string()
        }
        let next_char = popped_char.unwrap();

        match next_char {
            '\0' | '(' | ')' | ',' => {
                self.text.insert(0, next_char);
                "".to_string()
            }
            c if is_whitespace(c) => "".to_string(),
            _ => next_char.to_string() + self.read_until_whitespace().as_slice(),
        }
    }
}

#[test]
fn test_tokenizer_empty() {
    let test_str = "";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![]);
}

#[test]
fn test_tokenizer_1word() {
    let test_str = "hello";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
    ]);
}

#[test]
fn test_tokenizer_2words() {
    let test_str = "hello world";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("hello".to_string()),
        Token::Word("world".to_string()),
    ]);
}

#[test]
fn test_tokenizer_1number() {
    let test_str = "4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_1number_plus() {
    let test_str = "+4.2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(4.2),
    ]);
}

#[test]
fn test_tokenizer_2numbers() {
    let test_str = ".4 -2";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Number(0.4),
        Token::Number(-2.0),
    ]);
}

#[test]
fn test_tokenizer_point() {
    let test_str = "POINT (10 -20)";
    let tokens: Vec<Token> = tokenize(test_str).collect();
    assert_eq!(tokens, vec![
        Token::Word("POINT".to_string()),
        Token::ParenOpen,
        Token::Number(10.0),
        Token::Number(-20.0),
        Token::ParenClose,
    ]);
}
<|endoftext|>


<commit_before>//! Locating various executables part of a C toolchain.

use std::path::PathBuf;

use rustc_middle::bug;
use rustc_session::Session;
use rustc_target::spec::LinkerFlavor;

/// Tries to infer the path of a binary for the target toolchain from the linker name.
pub(crate) fn get_toolchain_binary(sess: &Session, tool: &str) -> PathBuf {
    let (mut linker, _linker_flavor) = linker_and_flavor(sess);
    let linker_file_name = linker
        .file_name()
        .and_then(|name| name.to_str())
        .unwrap_or_else(|| sess.fatal("couldn't extract file name from specified linker"));

    if linker_file_name == "ld.lld" {
        if tool != "ld" {
            linker.set_file_name(tool)
        }
    } else {
        let tool_file_name = linker_file_name
            .replace("ld", tool)
            .replace("gcc", tool)
            .replace("clang", tool)
            .replace("cc", tool);

        linker.set_file_name(tool_file_name)
    }

    linker
}

// Adapted from https://github.com/rust-lang/rust/blob/5db778affee7c6600c8e7a177c48282dab3f6292/src/librustc_codegen_ssa/back/link.rs#L848-L931
fn linker_and_flavor(sess: &Session) -> (PathBuf, LinkerFlavor) {
    fn infer_from(
        sess: &Session,
        linker: Option<PathBuf>,
        flavor: Option<LinkerFlavor>,
    ) -> Option<(PathBuf, LinkerFlavor)> {
        match (linker, flavor) {
            (Some(linker), Some(flavor)) => Some((linker, flavor)),
            // only the linker flavor is known; use the default linker for the selected flavor
            (None, Some(flavor)) => Some((
                PathBuf::from(match flavor {
                    LinkerFlavor::Em => {
                        if cfg!(windows) {
                            "emcc.bat"
                        } else {
                            "emcc"
                        }
                    }
                    LinkerFlavor::Gcc => {
                        if cfg!(any(target_os = "solaris", target_os = "illumos")) {
                            // On historical Solaris systems, "cc" may have
                            // been Sun Studio, which is not flag-compatible
                            // with "gcc".  This history casts a long shadow,
                            // and many modern illumos distributions today
                            // ship GCC as "gcc" without also making it
                            // available as "cc".
                            "gcc"
                        } else {
                            "cc"
                        }
                    }
                    LinkerFlavor::Ld => "ld",
                    LinkerFlavor::Msvc => "link.exe",
                    LinkerFlavor::Lld(_) => "lld",
                    LinkerFlavor::PtxLinker => "rust-ptx-linker",
                    LinkerFlavor::BpfLinker => "bpf-linker",
                }),
                flavor,
            )),
            (Some(linker), None) => {
                let stem = linker.file_stem().and_then(|stem| stem.to_str()).unwrap_or_else(|| {
                    sess.fatal("couldn't extract file stem from specified linker")
                });

                let flavor = if stem == "emcc" {
                    LinkerFlavor::Em
                } else if stem == "gcc"
                    || stem.ends_with("-gcc")
                    || stem == "clang"
                    || stem.ends_with("-clang")
                {
                    LinkerFlavor::Gcc
                } else if stem == "ld" || stem == "ld.lld" || stem.ends_with("-ld") {
                    LinkerFlavor::Ld
                } else if stem == "link" || stem == "lld-link" {
                    LinkerFlavor::Msvc
                } else if stem == "lld" || stem == "rust-lld" {
                    LinkerFlavor::Lld(sess.target.lld_flavor)
                } else {
                    // fall back to the value in the target spec
                    sess.target.linker_flavor
                };

                Some((linker, flavor))
            }
            (None, None) => None,
        }
    }

    // linker and linker flavor specified via command line have precedence over what the target
    // specification specifies
    if let Some(ret) = infer_from(sess, sess.opts.cg.linker.clone(), sess.opts.cg.linker_flavor) {
        return ret;
    }

    if let Some(ret) = infer_from(
        sess,
        sess.target.linker.clone().map(PathBuf::from),
        Some(sess.target.linker_flavor),
    ) {
        return ret;
    }

    bug!("Not enough information provided to determine how to invoke the linker");
}
<commit_msg>Rollup merge of #86016 - luqmana:infer-linker-flavor, r=petrochenkov<commit_after>//! Locating various executables part of a C toolchain.

use std::path::PathBuf;

use rustc_codegen_ssa::back::link::linker_and_flavor;
use rustc_session::Session;

/// Tries to infer the path of a binary for the target toolchain from the linker name.
pub(crate) fn get_toolchain_binary(sess: &Session, tool: &str) -> PathBuf {
    let (mut linker, _linker_flavor) = linker_and_flavor(sess);
    let linker_file_name = linker
        .file_name()
        .and_then(|name| name.to_str())
        .unwrap_or_else(|| sess.fatal("couldn't extract file name from specified linker"));

    if linker_file_name == "ld.lld" {
        if tool != "ld" {
            linker.set_file_name(tool)
        }
    } else {
        let tool_file_name = linker_file_name
            .replace("ld", tool)
            .replace("gcc", tool)
            .replace("clang", tool)
            .replace("cc", tool);

        linker.set_file_name(tool_file_name)
    }

    linker
}
<|endoftext|>


<commit_before>use rustc_middle::mir;

use crate::*;
use crate::helpers::check_arg_count;
use shims::posix::fs::EvalContextExt as _;
use shims::posix::linux::sync::futex;
use shims::posix::sync::EvalContextExt as _;
use shims::posix::thread::EvalContextExt as _;

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();

        match link_name {
            // errno
            "__errno_location" => {
                let &[] = check_arg_count(args)?;
                let errno_place = this.last_error_place()?;
                this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
            }

            // File related shims (but also see "syscall" below for statx)
            // These symbols have different names on Linux and macOS, which is the only reason they are not
            // in the `posix` module.
            "close" => {
                let &[fd] = check_arg_count(args)?;
                let result = this.close(fd)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "opendir" => {
                let &[name] = check_arg_count(args)?;
                let result = this.opendir(name)?;
                this.write_scalar(result, dest)?;
            }
            "readdir64_r" => {
                let &[dirp, entry, result] = check_arg_count(args)?;
                let result = this.linux_readdir64_r(dirp, entry, result)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "ftruncate64" => {
                let &[fd, length] = check_arg_count(args)?;
                let result = this.ftruncate64(fd, length)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            // Linux-only
            "posix_fadvise" => {
                let &[fd, offset, len, advice] = check_arg_count(args)?;
                this.read_scalar(fd)?.to_i32()?;
                this.read_scalar(offset)?.to_machine_isize(this)?;
                this.read_scalar(len)?.to_machine_isize(this)?;
                this.read_scalar(advice)?.to_i32()?;
                // fadvise is only informational, we can ignore it.
                this.write_null(dest)?;
            }
            "sync_file_range" => {
                let &[fd, offset, nbytes, flags] = check_arg_count(args)?;
                let result = this.sync_file_range(fd, offset, nbytes, flags)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Time related shims
            "clock_gettime" => {
                // This is a POSIX function but it has only been tested on linux.
                let &[clk_id, tp] = check_arg_count(args)?;
                let result = this.clock_gettime(clk_id, tp)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Querying system information
            "pthread_attr_getstack" => {
                // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
                let &[attr_place, addr_place, size_place] = check_arg_count(args)?;
                this.deref_operand(attr_place)?;
                let addr_place = this.deref_operand(addr_place)?;
                let size_place = this.deref_operand(size_place)?;

                this.write_scalar(
                    Scalar::from_uint(STACK_ADDR, this.pointer_size()),
                    addr_place.into(),
                )?;
                this.write_scalar(
                    Scalar::from_uint(STACK_SIZE, this.pointer_size()),
                    size_place.into(),
                )?;

                // Return success (`0`).
                this.write_null(dest)?;
            }

            // Threading
            "prctl" => {
                let &[option, arg2, arg3, arg4, arg5] = check_arg_count(args)?;
                let result = this.prctl(option, arg2, arg3, arg4, arg5)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_setclock" => {
                let &[attr, clock_id] = check_arg_count(args)?;
                let result = this.pthread_condattr_setclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_getclock" => {
                let &[attr, clock_id] = check_arg_count(args)?;
                let result = this.pthread_condattr_getclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Dynamically invoked syscalls
            "syscall" => {
                // FIXME: The libc syscall() function is a variadic function.
                // It's valid to call it with more arguments than a syscall
                // needs, so none of these syscalls should use check_arg_count.
                // However, depending on the calling convention it might depend
                // on the type and size of the arguments whether a call with
                // the wrong number of arguments (or types) is valid or not.
                // So this needs to be researched first.
                let sys_getrandom = this
                    .eval_libc("SYS_getrandom")?
                    .to_machine_usize(this)?;

                let sys_statx = this
                    .eval_libc("SYS_statx")?
                    .to_machine_usize(this)?;

                let sys_futex = this
                    .eval_libc("SYS_futex")?
                    .to_machine_usize(this)?;

                if args.is_empty() {
                    throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
                }
                match this.read_scalar(args[0])?.to_machine_usize(this)? {
                    // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
                    // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
                    id if id == sys_getrandom => {
                        // The first argument is the syscall id, so skip over it.
                        let &[_, ptr, len, flags] = check_arg_count(args)?;
                        getrandom(this, ptr, len, flags, dest)?;
                    }
                    // `statx` is used by `libstd` to retrieve metadata information on `linux`
                    // instead of using `stat`,`lstat` or `fstat` as on `macos`.
                    id if id == sys_statx => {
                        // The first argument is the syscall id, so skip over it.
                        let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?;
                        let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
                        this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
                    }
                    // `futex` is used by some synchonization primitives.
                    id if id == sys_futex => {
                        futex(this, args, dest)?;
                    }
                    id => throw_unsup_format!("miri does not support syscall ID {}", id),
                }
            }

            // Miscelanneous
            "getrandom" => {
                let &[ptr, len, flags] = check_arg_count(args)?;
                getrandom(this, ptr, len, flags, dest)?;
            }
            "sched_getaffinity" => {
                let &[pid, cpusetsize, mask] = check_arg_count(args)?;
                this.read_scalar(pid)?.to_i32()?;
                this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
                this.deref_operand(mask)?;
                // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
                let einval = this.eval_libc("EINVAL")?;
                this.set_last_error(einval)?;
                this.write_scalar(Scalar::from_i32(-1), dest)?;
            }

            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
            // These shims are enabled only when the caller is in the standard library.
            "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
                let &[_thread, _attr] = check_arg_count(args)?;
                this.write_null(dest)?;
            }

            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
        };

        Ok(true)
    }
}

// Shims the linux `getrandom` syscall.
fn getrandom<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    ptr: OpTy<'tcx, Tag>,
    len: OpTy<'tcx, Tag>,
    flags: OpTy<'tcx, Tag>,
    dest: PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    let ptr = this.read_scalar(ptr)?.check_init()?;
    let len = this.read_scalar(len)?.to_machine_usize(this)?;

    // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
    // neither of which have any effect on our current PRNG.
    this.read_scalar(flags)?.to_i32()?;

    this.gen_random(ptr, len)?;
    this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
    Ok(())
}
<commit_msg>Update syscall FIXME to include note about 'wrong' types.<commit_after>use rustc_middle::mir;

use crate::*;
use crate::helpers::check_arg_count;
use shims::posix::fs::EvalContextExt as _;
use shims::posix::linux::sync::futex;
use shims::posix::sync::EvalContextExt as _;
use shims::posix::thread::EvalContextExt as _;

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
    fn emulate_foreign_item_by_name(
        &mut self,
        link_name: &str,
        args: &[OpTy<'tcx, Tag>],
        dest: PlaceTy<'tcx, Tag>,
        _ret: mir::BasicBlock,
    ) -> InterpResult<'tcx, bool> {
        let this = self.eval_context_mut();

        match link_name {
            // errno
            "__errno_location" => {
                let &[] = check_arg_count(args)?;
                let errno_place = this.last_error_place()?;
                this.write_scalar(errno_place.to_ref().to_scalar()?, dest)?;
            }

            // File related shims (but also see "syscall" below for statx)
            // These symbols have different names on Linux and macOS, which is the only reason they are not
            // in the `posix` module.
            "close" => {
                let &[fd] = check_arg_count(args)?;
                let result = this.close(fd)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "opendir" => {
                let &[name] = check_arg_count(args)?;
                let result = this.opendir(name)?;
                this.write_scalar(result, dest)?;
            }
            "readdir64_r" => {
                let &[dirp, entry, result] = check_arg_count(args)?;
                let result = this.linux_readdir64_r(dirp, entry, result)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "ftruncate64" => {
                let &[fd, length] = check_arg_count(args)?;
                let result = this.ftruncate64(fd, length)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            // Linux-only
            "posix_fadvise" => {
                let &[fd, offset, len, advice] = check_arg_count(args)?;
                this.read_scalar(fd)?.to_i32()?;
                this.read_scalar(offset)?.to_machine_isize(this)?;
                this.read_scalar(len)?.to_machine_isize(this)?;
                this.read_scalar(advice)?.to_i32()?;
                // fadvise is only informational, we can ignore it.
                this.write_null(dest)?;
            }
            "sync_file_range" => {
                let &[fd, offset, nbytes, flags] = check_arg_count(args)?;
                let result = this.sync_file_range(fd, offset, nbytes, flags)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Time related shims
            "clock_gettime" => {
                // This is a POSIX function but it has only been tested on linux.
                let &[clk_id, tp] = check_arg_count(args)?;
                let result = this.clock_gettime(clk_id, tp)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Querying system information
            "pthread_attr_getstack" => {
                // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
                let &[attr_place, addr_place, size_place] = check_arg_count(args)?;
                this.deref_operand(attr_place)?;
                let addr_place = this.deref_operand(addr_place)?;
                let size_place = this.deref_operand(size_place)?;

                this.write_scalar(
                    Scalar::from_uint(STACK_ADDR, this.pointer_size()),
                    addr_place.into(),
                )?;
                this.write_scalar(
                    Scalar::from_uint(STACK_SIZE, this.pointer_size()),
                    size_place.into(),
                )?;

                // Return success (`0`).
                this.write_null(dest)?;
            }

            // Threading
            "prctl" => {
                let &[option, arg2, arg3, arg4, arg5] = check_arg_count(args)?;
                let result = this.prctl(option, arg2, arg3, arg4, arg5)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_setclock" => {
                let &[attr, clock_id] = check_arg_count(args)?;
                let result = this.pthread_condattr_setclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }
            "pthread_condattr_getclock" => {
                let &[attr, clock_id] = check_arg_count(args)?;
                let result = this.pthread_condattr_getclock(attr, clock_id)?;
                this.write_scalar(Scalar::from_i32(result), dest)?;
            }

            // Dynamically invoked syscalls
            "syscall" => {
                // FIXME: The libc syscall() function is a variadic function.
                // It's valid to call it with more arguments than a syscall
                // needs, so none of these syscalls should use check_arg_count.
                // It's even valid to call it with the wrong type of arguments,
                // as long as they'd end up in the same place with the calling
                // convention used. (E.g. using a `usize` instead of a pointer.)
                // It's not directly clear which number, size, and type of arguments
                // are acceptable in which cases and which aren't. (E.g. some
                // types might take up the space of two registers.)
                // So this needs to be researched first.

                let sys_getrandom = this
                    .eval_libc("SYS_getrandom")?
                    .to_machine_usize(this)?;

                let sys_statx = this
                    .eval_libc("SYS_statx")?
                    .to_machine_usize(this)?;

                let sys_futex = this
                    .eval_libc("SYS_futex")?
                    .to_machine_usize(this)?;

                if args.is_empty() {
                    throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
                }
                match this.read_scalar(args[0])?.to_machine_usize(this)? {
                    // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
                    // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
                    id if id == sys_getrandom => {
                        // The first argument is the syscall id, so skip over it.
                        let &[_, ptr, len, flags] = check_arg_count(args)?;
                        getrandom(this, ptr, len, flags, dest)?;
                    }
                    // `statx` is used by `libstd` to retrieve metadata information on `linux`
                    // instead of using `stat`,`lstat` or `fstat` as on `macos`.
                    id if id == sys_statx => {
                        // The first argument is the syscall id, so skip over it.
                        let &[_, dirfd, pathname, flags, mask, statxbuf] = check_arg_count(args)?;
                        let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
                        this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
                    }
                    // `futex` is used by some synchonization primitives.
                    id if id == sys_futex => {
                        futex(this, args, dest)?;
                    }
                    id => throw_unsup_format!("miri does not support syscall ID {}", id),
                }
            }

            // Miscelanneous
            "getrandom" => {
                let &[ptr, len, flags] = check_arg_count(args)?;
                getrandom(this, ptr, len, flags, dest)?;
            }
            "sched_getaffinity" => {
                let &[pid, cpusetsize, mask] = check_arg_count(args)?;
                this.read_scalar(pid)?.to_i32()?;
                this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
                this.deref_operand(mask)?;
                // FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
                let einval = this.eval_libc("EINVAL")?;
                this.set_last_error(einval)?;
                this.write_scalar(Scalar::from_i32(-1), dest)?;
            }

            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
            // These shims are enabled only when the caller is in the standard library.
            "pthread_getattr_np" if this.frame().instance.to_string().starts_with("std::sys::unix::") => {
                let &[_thread, _attr] = check_arg_count(args)?;
                this.write_null(dest)?;
            }

            _ => throw_unsup_format!("can't call foreign function: {}", link_name),
        };

        Ok(true)
    }
}

// Shims the linux `getrandom` syscall.
fn getrandom<'tcx>(
    this: &mut MiriEvalContext<'_, 'tcx>,
    ptr: OpTy<'tcx, Tag>,
    len: OpTy<'tcx, Tag>,
    flags: OpTy<'tcx, Tag>,
    dest: PlaceTy<'tcx, Tag>,
) -> InterpResult<'tcx> {
    let ptr = this.read_scalar(ptr)?.check_init()?;
    let len = this.read_scalar(len)?.to_machine_usize(this)?;

    // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
    // neither of which have any effect on our current PRNG.
    this.read_scalar(flags)?.to_i32()?;

    this.gen_random(ptr, len)?;
    this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
    Ok(())
}
<|endoftext|>


<commit_before>
use vertex::{RawVertex, Vertex};
use edge::{RawEdge, Edge};
use graph::TraversableToVertex;
use path::{Path, Element};
use std::iter::Extend;
use std::iter::Filter;

// use std::iter::Iterator;

// define a traversal as a series of stages

pub struct GraphQuery {
    // many paths
    pub paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<Vertex>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
            let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

    pub fn get_paths(&self) -> &Vec<Path> {
        &self.paths
    }
    pub fn push(&mut self, path:Path) {
        self.paths.push(path)
    }

    /*
    generic mapper for our queries
    future this may use worker threads to perform
    traversals

    TODO make sure that if nothing comes back we don't keep it in the path

     */
    fn map<F: Fn(&Path) -> Vec<Element>>(&self, closure: F) -> GraphQuery  {
        let mut result = GraphQuery::empty(); // result
        for path in self.paths.iter() {
            let mut tmp = closure(&path);
            // let mut tmp2 = tmp.as_slice();
            // currently gets back a Vec<Path> but what if it gets Elements?
            let new_elements = path.permute(&tmp);
            result.paths.extend(new_elements);
        }
        result
    }

    pub fn outV(&self, labels: &[&str]) -> GraphQuery {
        // map can figure out the rest
        let f = |path: &Path| {
            //take the final element in the path
            let element = path.last().unwrap();

            let result : Vec<Element> = match element {
                &Element::Vertex(v) =>
                    v.outV(&labels).iter().map(|v| Element::Vertex(*v)).collect(),
                    // Vec::new(),
                &Element::Edge(ref e) =>
                    Vec::new()
            };
            // apply outV
            result
        };
        self.map(f)
    }

    pub fn filter<F: Fn(&Path) -> bool>(&self, closure: F) -> GraphQuery {
        let result = GraphQuery::empty();
        // apply the filter to each path
        let tmp = self.paths.iter().filter(|&x| true);
        // result.paths = tmp;
        return result
    }

//    pub fn inV(&self) -> GraphQuery {
//        let g = GraphQuery::empty();
//        let f = |element: &Element| {
//            element.inV()
//        };
//        self.map(f)
//    }
}
<commit_msg>query filtering is kind of jacked...<commit_after>
use vertex::{RawVertex, Vertex};
use edge::{RawEdge, Edge};
use graph::TraversableToVertex;
use path::{Path, Element};
use std::iter::Extend;
use std::iter::Filter;

// use std::iter::Iterator;

// define a traversal as a series of stages

pub struct GraphQuery {
    // many paths
    pub paths: Vec<Path>
}

impl GraphQuery {
    pub fn new(vertices: Vec<Vertex>) -> GraphQuery {
        // create a new path for each proxy, add the proxy
        let mut paths = Vec::new();
        for p in vertices {
            let mut path = Path::new(p);
            paths.push(path)
        }
        GraphQuery{paths:paths}
    }

    pub fn empty() -> GraphQuery {
        // return an empty graph query
        GraphQuery{paths:Vec::new()}
    }

    pub fn get_paths(&self) -> &Vec<Path> {
        &self.paths
    }
    pub fn push(&mut self, path:Path) {
        self.paths.push(path)
    }

    /*
    generic mapper for our queries
    future this may use worker threads to perform
    traversals

    TODO make sure that if nothing comes back we don't keep it in the path

     */
    fn map<F: Fn(&Path) -> Vec<Element>>(&self, closure: F) -> GraphQuery  {
        let mut result = GraphQuery::empty(); // result
        for path in self.paths.iter() {
            let mut tmp = closure(&path);
            // let mut tmp2 = tmp.as_slice();
            // currently gets back a Vec<Path> but what if it gets Elements?
            let new_elements = path.permute(&tmp);
            result.paths.extend(new_elements);
        }
        result
    }

    pub fn outV(&self, labels: &[&str]) -> GraphQuery {
        // map can figure out the rest
        let f = |path: &Path| {
            //take the final element in the path
            let element = path.last().unwrap();

            let result : Vec<Element> = match element {
                &Element::Vertex(v) =>
                    v.outV(&labels).iter().map(|v| Element::Vertex(*v)).collect(),
                    // Vec::new(),
                &Element::Edge(ref e) =>
                    Vec::new()
            };
            // apply outV
            result
        };
        self.map(f)
    }

    pub fn filter<F: Fn(&Path) -> bool>(&self, closure: F) -> GraphQuery {
        let result = GraphQuery::empty();
        // apply the filter to each path
        let tmp : Vec<Path> = self.paths.iter().filter(closure).collect();
        result.paths = tmp;
        return result
    }

//    pub fn inV(&self) -> GraphQuery {
//        let g = GraphQuery::empty();
//        let f = |element: &Element| {
//            element.inV()
//        };
//        self.map(f)
//    }
}
<|endoftext|>


<commit_before>#[derive(Clone, Debug)]
pub struct Decision {
    x: usize,
    r: usize,
}

const ALL_VALID_DECISIONS: &'static [Decision] = &[
    Decision { x: 1, r: 0 },
    Decision { x: 1, r: 1 },
    Decision { x: 1, r: 2 },
    // Decision { x: 1, r: 3 },
    Decision { x: 2, r: 0 },
    Decision { x: 2, r: 1 },
    Decision { x: 2, r: 2 },
    Decision { x: 2, r: 3 },
    Decision { x: 3, r: 0 },
    Decision { x: 3, r: 1 },
    Decision { x: 3, r: 2 },
    Decision { x: 3, r: 3 },
    Decision { x: 4, r: 0 },
    Decision { x: 4, r: 1 },
    Decision { x: 4, r: 2 },
    Decision { x: 4, r: 3 },
    Decision { x: 5, r: 0 },
    Decision { x: 5, r: 1 },
    Decision { x: 5, r: 2 },
    Decision { x: 5, r: 3 },
    Decision { x: 6, r: 0 },
    // Decision { x: 6, r: 1 },
    Decision { x: 6, r: 2 },
    Decision { x: 6, r: 3 },
];

impl Decision {
    pub fn new(x: usize, r: usize) -> Decision {
        Decision {
            x: x,
            r: r,
        }
    }

    pub fn all_valid_decisions() -> &'static [Decision] {
        ALL_VALID_DECISIONS
    }

    pub fn axis_x(&self) -> usize {
        self.x
    }

    pub fn child_x(&self) -> usize {
        match self.r {
            0 => self.x,
            1 => self.x + 1,
            2 => self.x,
            3 => self.x - 1,
            _ => {
                assert!(false, "unexpected r={}", self.r);
                unreachable!()
            }
        }
    }

    pub fn rot(&self) -> usize {
        self.r
    }

    pub fn valid(&self) -> bool {
        if self.x <= 0 || 6 < self.x || 4 <= self.r {
            return false;
        }
        if (self.x == 1 && self.r == 3) || (self.x == 6 && self.r == 1) {
            return false;
        }

        true
    }
}

#[cfg(test)]
mod tests {
    use decision::Decision;

    #[test]
    fn test_all_valid_decisions() {
        assert_eq!(22, Decision::all_valid_decisions().len());
    }

    #[test]
    fn test_xr() {
        let d10 = Decision::new(3, 0);
        let d11 = Decision::new(3, 1);
        let d12 = Decision::new(3, 2);
        let d13 = Decision::new(3, 3);

        assert_eq!(3, d10.axis_x());
        assert_eq!(3, d10.child_x());
        assert_eq!(0, d10.rot());

        assert_eq!(3, d11.axis_x());
        assert_eq!(4, d11.child_x());
        assert_eq!(1, d11.rot());

        assert_eq!(3, d12.axis_x());
        assert_eq!(3, d12.child_x());
        assert_eq!(2, d12.rot());

        assert_eq!(3, d13.axis_x());
        assert_eq!(2, d13.child_x());
        assert_eq!(3, d13.rot());
    }

    #[test]
    fn test_valid() {
        assert!(Decision::new(1, 0).valid());
        assert!(Decision::new(1, 1).valid());
        assert!(Decision::new(1, 2).valid());
        assert!(!Decision::new(1, 3).valid());
        assert!(!Decision::new(1, 4).valid());

        assert!(Decision::new(2, 0).valid());
        assert!(Decision::new(2, 1).valid());
        assert!(Decision::new(2, 2).valid());
        assert!(Decision::new(2, 3).valid());
        assert!(!Decision::new(2, 4).valid());

        assert!(Decision::new(6, 0).valid());
        assert!(!Decision::new(6, 1).valid());
        assert!(Decision::new(6, 2).valid());
        assert!(Decision::new(6, 3).valid());
        assert!(!Decision::new(6, 4).valid());
    }
}
<commit_msg>Implement PartialEq trait to Decision<commit_after>#[derive(Clone, Debug, PartialEq)]
pub struct Decision {
    x: usize,
    r: usize,
}

const ALL_VALID_DECISIONS: &'static [Decision] = &[
    Decision { x: 1, r: 0 },
    Decision { x: 1, r: 1 },
    Decision { x: 1, r: 2 },
    // Decision { x: 1, r: 3 },
    Decision { x: 2, r: 0 },
    Decision { x: 2, r: 1 },
    Decision { x: 2, r: 2 },
    Decision { x: 2, r: 3 },
    Decision { x: 3, r: 0 },
    Decision { x: 3, r: 1 },
    Decision { x: 3, r: 2 },
    Decision { x: 3, r: 3 },
    Decision { x: 4, r: 0 },
    Decision { x: 4, r: 1 },
    Decision { x: 4, r: 2 },
    Decision { x: 4, r: 3 },
    Decision { x: 5, r: 0 },
    Decision { x: 5, r: 1 },
    Decision { x: 5, r: 2 },
    Decision { x: 5, r: 3 },
    Decision { x: 6, r: 0 },
    // Decision { x: 6, r: 1 },
    Decision { x: 6, r: 2 },
    Decision { x: 6, r: 3 },
];

impl Decision {
    pub fn new(x: usize, r: usize) -> Decision {
        Decision {
            x: x,
            r: r,
        }
    }

    pub fn all_valid_decisions() -> &'static [Decision] {
        ALL_VALID_DECISIONS
    }

    pub fn axis_x(&self) -> usize {
        self.x
    }

    pub fn child_x(&self) -> usize {
        match self.r {
            0 => self.x,
            1 => self.x + 1,
            2 => self.x,
            3 => self.x - 1,
            _ => {
                assert!(false, "unexpected r={}", self.r);
                unreachable!()
            }
        }
    }

    pub fn rot(&self) -> usize {
        self.r
    }

    pub fn valid(&self) -> bool {
        if self.x <= 0 || 6 < self.x || 4 <= self.r {
            return false;
        }
        if (self.x == 1 && self.r == 3) || (self.x == 6 && self.r == 1) {
            return false;
        }

        true
    }
}

#[cfg(test)]
mod tests {
    use decision::Decision;

    #[test]
    fn test_all_valid_decisions() {
        assert_eq!(22, Decision::all_valid_decisions().len());
    }

    #[test]
    fn test_xr() {
        let d10 = Decision::new(3, 0);
        let d11 = Decision::new(3, 1);
        let d12 = Decision::new(3, 2);
        let d13 = Decision::new(3, 3);

        assert_eq!(3, d10.axis_x());
        assert_eq!(3, d10.child_x());
        assert_eq!(0, d10.rot());

        assert_eq!(3, d11.axis_x());
        assert_eq!(4, d11.child_x());
        assert_eq!(1, d11.rot());

        assert_eq!(3, d12.axis_x());
        assert_eq!(3, d12.child_x());
        assert_eq!(2, d12.rot());

        assert_eq!(3, d13.axis_x());
        assert_eq!(2, d13.child_x());
        assert_eq!(3, d13.rot());
    }

    #[test]
    fn test_valid() {
        assert!(Decision::new(1, 0).valid());
        assert!(Decision::new(1, 1).valid());
        assert!(Decision::new(1, 2).valid());
        assert!(!Decision::new(1, 3).valid());
        assert!(!Decision::new(1, 4).valid());

        assert!(Decision::new(2, 0).valid());
        assert!(Decision::new(2, 1).valid());
        assert!(Decision::new(2, 2).valid());
        assert!(Decision::new(2, 3).valid());
        assert!(!Decision::new(2, 4).valid());

        assert!(Decision::new(6, 0).valid());
        assert!(!Decision::new(6, 1).valid());
        assert!(Decision::new(6, 2).valid());
        assert!(Decision::new(6, 3).valid());
        assert!(!Decision::new(6, 4).valid());
    }
}
<|endoftext|>


<commit_before>use anyhow::Result;
use reqwest::{blocking::Client, header};

use crate::util::errors::{internal, AppResult};

use std::env;
use std::fs::{self, File};
use std::io::{Cursor, SeekFrom};
use std::path::PathBuf;

use crate::models::Crate;

const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
const CACHE_CONTROL_README: &str = "public,max-age=604800";
const CACHE_CONTROL_INDEX: &str = "public,max-age=600";

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with `TestApp::with_proxy()`, the recording proxy is used.
    S3 {
        bucket: Box<s3::Bucket>,
        index_bucket: Option<Box<s3::Bucket>>,
        cdn: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,
}

pub enum UploadBucket {
    Default,
    Index,
}

impl Uploader {
    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::crate_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::crate_path(crate_name, version)),
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::readme_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::readme_path(crate_name, version)),
        }
    }

    /// Returns the internal path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        format!("crates/{name}/{name}-{version}.crate")
    }

    /// Returns the internal path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{name}/{name}-{version}.html")
    }

    /// Returns the internal path of an uploaded crate's index file.
    fn index_path(name: &str) -> String {
        cargo_registry_index::Repository::relative_index_file_for_url(name)
    }

    /// Returns the absolute path to the locally uploaded file.
    fn local_uploads_path(path: &str, upload_bucket: UploadBucket) -> PathBuf {
        let path = match upload_bucket {
            UploadBucket::Index => PathBuf::from("index").join(path),
            UploadBucket::Default => PathBuf::from(path),
        };
        env::current_dir().unwrap().join("local_uploads").join(path)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local`).
    ///
    /// It returns the path of the uploaded file.
    ///
    /// # Panics
    ///
    /// This function can panic on an `Self::Local` during development.
    /// Production and tests use `Self::S3` which should not panic.
    pub fn upload<R: std::io::Read + std::io::Seek + Send + 'static>(
        &self,
        client: &Client,
        path: &str,
        mut content: R,
        content_type: &str,
        extra_headers: header::HeaderMap,
        upload_bucket: UploadBucket,
    ) -> Result<Option<String>> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    let content_length = content.seek(SeekFrom::End(0))?;
                    content.seek(SeekFrom::Start(0))?;
                    bucket.put(
                        client,
                        path,
                        content,
                        content_length,
                        content_type,
                        extra_headers,
                    )?;
                }

                Ok(Some(String::from(path)))
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                std::io::copy(&mut content, &mut file)?;
                Ok(filename.to_str().map(String::from))
            }
        }
    }

    /// Deletes a file using the configured uploader (either `S3`, `Local`).
    pub fn delete(&self, client: &Client, path: &str, upload_bucket: UploadBucket) -> Result<()> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    bucket.delete(client, path)?;
                }
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                std::fs::remove_file(&filename)?;
            }
        }
        Ok(())
    }

    /// Uploads a crate and returns the checksum of the uploaded crate file.
    pub fn upload_crate(
        &self,
        http_client: &Client,
        body: Vec<u8>,
        krate: &Crate,
        vers: &semver::Version,
    ) -> AppResult<()> {
        let path = Uploader::crate_path(&krate.name, &vers.to_string());
        let content = Cursor::new(body);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_IMMUTABLE),
        );
        self.upload(
            http_client,
            &path,
            content,
            "application/gzip",
            extra_headers,
            UploadBucket::Default,
        )
        .map_err(|e| internal(&format_args!("failed to upload crate: {}", e)))?;
        Ok(())
    }

    pub(crate) fn upload_readme(
        &self,
        http_client: &Client,
        crate_name: &str,
        vers: &str,
        readme: String,
    ) -> Result<()> {
        let path = Uploader::readme_path(crate_name, vers);
        let content = Cursor::new(readme);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_README),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/html",
            extra_headers,
            UploadBucket::Default,
        )?;
        Ok(())
    }

    pub(crate) fn upload_index(
        &self,
        http_client: &Client,
        crate_name: &str,
        index: String,
    ) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        let content = Cursor::new(index);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_INDEX),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/plain",
            extra_headers,
            UploadBucket::Index,
        )?;
        Ok(())
    }

    pub(crate) fn delete_index(&self, http_client: &Client, crate_name: &str) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        self.delete(http_client, &path, UploadBucket::Index)?;
        Ok(())
    }
}
<commit_msg>Locally, ignore errors when deleting the index file<commit_after>use anyhow::Result;
use reqwest::{blocking::Client, header};

use crate::util::errors::{internal, AppResult};

use std::env;
use std::fs::{self, File};
use std::io::{Cursor, SeekFrom};
use std::path::PathBuf;

use crate::models::Crate;

const CACHE_CONTROL_IMMUTABLE: &str = "public,max-age=31536000,immutable";
const CACHE_CONTROL_README: &str = "public,max-age=604800";
const CACHE_CONTROL_INDEX: &str = "public,max-age=600";

#[derive(Clone, Debug)]
pub enum Uploader {
    /// For production usage, uploads and redirects to s3.
    /// For test usage with `TestApp::with_proxy()`, the recording proxy is used.
    S3 {
        bucket: Box<s3::Bucket>,
        index_bucket: Option<Box<s3::Bucket>>,
        cdn: Option<String>,
    },

    /// For development usage only: "uploads" crate files to `dist` and serves them
    /// from there as well to enable local publishing and download
    Local,
}

pub enum UploadBucket {
    Default,
    Index,
}

impl Uploader {
    /// Returns the URL of an uploaded crate's version archive.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn crate_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::crate_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::crate_path(crate_name, version)),
        }
    }

    /// Returns the URL of an uploaded crate's version readme.
    ///
    /// The function doesn't check for the existence of the file.
    pub fn readme_location(&self, crate_name: &str, version: &str) -> String {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref cdn,
                ..
            } => {
                let host = match *cdn {
                    Some(ref s) => s.clone(),
                    None => bucket.host(),
                };
                let path = Uploader::readme_path(crate_name, version);
                format!("https://{host}/{path}")
            }
            Uploader::Local => format!("/{}", Uploader::readme_path(crate_name, version)),
        }
    }

    /// Returns the internal path of an uploaded crate's version archive.
    fn crate_path(name: &str, version: &str) -> String {
        format!("crates/{name}/{name}-{version}.crate")
    }

    /// Returns the internal path of an uploaded crate's version readme.
    fn readme_path(name: &str, version: &str) -> String {
        format!("readmes/{name}/{name}-{version}.html")
    }

    /// Returns the internal path of an uploaded crate's index file.
    fn index_path(name: &str) -> String {
        cargo_registry_index::Repository::relative_index_file_for_url(name)
    }

    /// Returns the absolute path to the locally uploaded file.
    fn local_uploads_path(path: &str, upload_bucket: UploadBucket) -> PathBuf {
        let path = match upload_bucket {
            UploadBucket::Index => PathBuf::from("index").join(path),
            UploadBucket::Default => PathBuf::from(path),
        };
        env::current_dir().unwrap().join("local_uploads").join(path)
    }

    /// Uploads a file using the configured uploader (either `S3`, `Local`).
    ///
    /// It returns the path of the uploaded file.
    ///
    /// # Panics
    ///
    /// This function can panic on an `Self::Local` during development.
    /// Production and tests use `Self::S3` which should not panic.
    pub fn upload<R: std::io::Read + std::io::Seek + Send + 'static>(
        &self,
        client: &Client,
        path: &str,
        mut content: R,
        content_type: &str,
        extra_headers: header::HeaderMap,
        upload_bucket: UploadBucket,
    ) -> Result<Option<String>> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    let content_length = content.seek(SeekFrom::End(0))?;
                    content.seek(SeekFrom::Start(0))?;
                    bucket.put(
                        client,
                        path,
                        content,
                        content_length,
                        content_type,
                        extra_headers,
                    )?;
                }

                Ok(Some(String::from(path)))
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                let dir = filename.parent().unwrap();
                fs::create_dir_all(dir)?;
                let mut file = File::create(&filename)?;
                std::io::copy(&mut content, &mut file)?;
                Ok(filename.to_str().map(String::from))
            }
        }
    }

    /// Deletes a file using the configured uploader (either `S3`, `Local`).
    pub fn delete(&self, client: &Client, path: &str, upload_bucket: UploadBucket) -> Result<()> {
        match *self {
            Uploader::S3 {
                ref bucket,
                ref index_bucket,
                ..
            } => {
                let bucket = match upload_bucket {
                    UploadBucket::Default => Some(bucket),
                    UploadBucket::Index => index_bucket.as_ref(),
                };

                if let Some(bucket) = bucket {
                    bucket.delete(client, path)?;
                }
            }
            Uploader::Local => {
                let filename = Self::local_uploads_path(path, upload_bucket);
                // Ignore errors if the local index file doesn't exist; this can happen if you
                // aren't running the background job worker locally
                let _ = std::fs::remove_file(&filename);
            }
        }
        Ok(())
    }

    /// Uploads a crate and returns the checksum of the uploaded crate file.
    pub fn upload_crate(
        &self,
        http_client: &Client,
        body: Vec<u8>,
        krate: &Crate,
        vers: &semver::Version,
    ) -> AppResult<()> {
        let path = Uploader::crate_path(&krate.name, &vers.to_string());
        let content = Cursor::new(body);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_IMMUTABLE),
        );
        self.upload(
            http_client,
            &path,
            content,
            "application/gzip",
            extra_headers,
            UploadBucket::Default,
        )
        .map_err(|e| internal(&format_args!("failed to upload crate: {}", e)))?;
        Ok(())
    }

    pub(crate) fn upload_readme(
        &self,
        http_client: &Client,
        crate_name: &str,
        vers: &str,
        readme: String,
    ) -> Result<()> {
        let path = Uploader::readme_path(crate_name, vers);
        let content = Cursor::new(readme);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_README),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/html",
            extra_headers,
            UploadBucket::Default,
        )?;
        Ok(())
    }

    pub(crate) fn upload_index(
        &self,
        http_client: &Client,
        crate_name: &str,
        index: String,
    ) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        let content = Cursor::new(index);
        let mut extra_headers = header::HeaderMap::new();
        extra_headers.insert(
            header::CACHE_CONTROL,
            header::HeaderValue::from_static(CACHE_CONTROL_INDEX),
        );
        self.upload(
            http_client,
            &path,
            content,
            "text/plain",
            extra_headers,
            UploadBucket::Index,
        )?;
        Ok(())
    }

    pub(crate) fn delete_index(&self, http_client: &Client, crate_name: &str) -> Result<()> {
        let path = Uploader::index_path(crate_name);
        self.delete(http_client, &path, UploadBucket::Index)?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::error::ParseMessageError;
use crate::structs;
use std::fmt;
use std::str::FromStr;

#[derive(Debug)]
pub enum Pw {
    Uid(u64),
    Nam(String),
    Ent(Ent),
}

#[derive(Debug)]
pub enum Sp {
    Nam(String),
    Ent(Ent),
}

#[derive(Debug)]
pub enum Gr {
    Gid(u64),
    Nam(String),
    Ent(Ent),
}

#[derive(Debug)]
pub enum Ent {
    Set(u32),
    Get(u32),
    End(u32),
}

pub struct DividedMessage {
    pub cont: bool,
    pub message: String,
}

impl DividedMessage {
    #[allow(dead_code)]
    pub fn new(msg: &str, size: usize) -> Vec<Self> {
        let mut msgs = vec![];
        let mut idx = 0;
        while idx + size < msg.len() {
            msgs.push(Self { cont: true,
                             message: msg[idx..idx + size].to_owned() });
            idx += size
        }
        msgs.push(Self { cont: false,
                         message: msg[idx..msg.len()].to_owned() });
        msgs
    }
}

impl fmt::Display for DividedMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", if self.cont { 1 } else { 0 }, self.message)
    }
}

impl FromStr for DividedMessage {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("0:") {
            Ok(Self { cont: false,
                      message: msg.to_owned() })
        } else if let Some(msg) = s.strip_prefix("1:") {
            Ok(Self { cont: true,
                      message: msg.to_owned() })
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

#[derive(Debug)]
pub enum ClientMessage {
    Cont,
    Key { user: String },
    Pam { user: String },
    CleanUp,
    RateLimit,
    SectorGroups,
    Pw(Pw),
    Sp(Sp),
    Gr(Gr),
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum DaemonMessage {
    Success,
    Error {
        message: String,
    },
    Key {
        keys: String,
    },
    Pam {
        result: bool,
    },
    RateLimit {
        limit: usize,
        remaining: usize,
        reset: usize,
    },
    SectorGroups {
        sectors: Vec<structs::SectorGroup>,
    },
    Pw {
        login: String,
        uid: u64,
        gid: u64,
        home: String,
        sh: String,
    },
    Sp {
        login: String,
        pass: String,
    },
    Gr {
        sector: structs::SectorGroup,
    },
}

impl fmt::Display for Ent {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Ent::Set(pid) => write!(f, "set|{}", pid),
            Ent::Get(pid) => write!(f, "get|{}", pid),
            Ent::End(pid) => write!(f, "end|{}", pid),
        }
    }
}

impl fmt::Display for Pw {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Pw::Uid(uid) => write!(f, "uid={}", uid),
            Pw::Nam(name) => write!(f, "name={}", name),
            Pw::Ent(ent) => write!(f, "ent={}", ent),
        }
    }
}

impl fmt::Display for Sp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Sp::Nam(name) => write!(f, "name={}", name),
            Sp::Ent(ent) => write!(f, "ent={}", ent),
        }
    }
}

impl fmt::Display for Gr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Gr::Gid(gid) => write!(f, "gid={}", gid),
            Gr::Nam(name) => write!(f, "name={}", name),
            Gr::Ent(ent) => write!(f, "ent={}", ent),
        }
    }
}

impl fmt::Display for ClientMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ClientMessage::Cont => write!(f, "c:cont"),
            ClientMessage::Key { user } => write!(f, "c:key:{}", user),
            ClientMessage::Pam { user } => write!(f, "c:pam:{}", user),
            ClientMessage::CleanUp => write!(f, "c:cleanup"),
            ClientMessage::RateLimit => write!(f, "c:ratelimit"),
            ClientMessage::SectorGroups => write!(f, "c:sectors"),
            ClientMessage::Pw(pw) => write!(f, "c:pw:{}", pw),
            ClientMessage::Sp(sp) => write!(f, "c:sp:{}", sp),
            ClientMessage::Gr(gr) => write!(f, "c:gr:{}", gr),
        }
    }
}

impl fmt::Display for DaemonMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DaemonMessage::Error { message } => write!(f, "d:error:{}", message),
            DaemonMessage::Success => write!(f, "d:success"),
            DaemonMessage::Key { keys } => write!(f, "d:key:{}", keys),
            DaemonMessage::Pam { result } => write!(f, "d:pam:{}", result),
            DaemonMessage::RateLimit { limit,
                                       remaining,
                                       reset, } => write!(f, "d:ratelimit:{}:{}:{}", limit, remaining, reset),
            DaemonMessage::SectorGroups { sectors } => {
                let ss: Vec<String> = sectors.iter().map(|s| s.to_string()).collect();
                write!(f, "d:sectors:{}", ss.join("\n"))
            }
            DaemonMessage::Pw { login,
                                uid,
                                gid,
                                home,
                                sh, } => write!(f, "d:pw:{}:{}:{}:{}:{}", login, uid, gid, home, sh),
            DaemonMessage::Sp { login, pass } => write!(f, "d:sp:{}:{}", login, pass),
            DaemonMessage::Gr { sector } => write!(f, "d:gr:{}", sector),
        }
    }
}

impl FromStr for Ent {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("set|") {
            Ok(Ent::Set(msg.parse::<u32>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("get|") {
            Ok(Ent::Get(msg.parse::<u32>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("end|") {
            Ok(Ent::End(msg.parse::<u32>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for Pw {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("uid=") {
            Ok(Pw::Uid(msg.parse::<u64>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("name=") {
            Ok(Pw::Nam(String::from(msg)))
        } else if let Some(msg) = s.strip_prefix("ent=") {
            Ok(Pw::Ent(msg.parse::<Ent>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for Sp {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("name=") {
            Ok(Sp::Nam(String::from(msg)))
        } else if let Some(msg) = s.strip_prefix("ent=") {
            Ok(Sp::Ent(msg.parse::<Ent>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for Gr {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("gid=") {
            Ok(Gr::Gid(msg.parse::<u64>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("name=") {
            Ok(Gr::Nam(String::from(msg)))
        } else if let Some(msg) = s.strip_prefix("ent=") {
            Ok(Gr::Ent(msg.parse::<Ent>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for ClientMessage {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(_) = s.strip_prefix("c:cont") {
            Ok(ClientMessage::Cont)
        } else if let Some(msg) = s.strip_prefix("c:key:") {
            Ok(ClientMessage::Key { user: String::from(msg) })
        } else if let Some(msg) = s.strip_prefix("c:pam:") {
            Ok(ClientMessage::Pam { user: String::from(msg) })
        } else if s == "c:cleanup" {
            Ok(ClientMessage::CleanUp)
        } else if s == "c:ratelimit" {
            Ok(ClientMessage::RateLimit)
        } else if s == "c:sectors" {
            Ok(ClientMessage::SectorGroups)
        } else if let Some(msg) = s.strip_prefix("c:pw:") {
            Ok(ClientMessage::Pw(msg.parse::<Pw>()?))
        } else if let Some(msg) = s.strip_prefix("c:sp:") {
            Ok(ClientMessage::Sp(msg.parse::<Sp>()?))
        } else if let Some(msg) = s.strip_prefix("c:gr:") {
            Ok(ClientMessage::Gr(msg.parse::<Gr>()?))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for DaemonMessage {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("d:key:") {
            Ok(DaemonMessage::Key { keys: String::from(msg) })
        } else if let Some(msg) = s.strip_prefix("d:pam:") {
            Ok(DaemonMessage::Pam { result: FromStr::from_str(msg).unwrap_or(false) })
        } else if s == "d:success" {
            Ok(DaemonMessage::Success)
        } else if let Some(msg) = s.strip_prefix("d:ratelimit:") {
            let fields: Vec<String> = msg.split(':').map(|s| s.to_string()).collect();
            if fields.len() < 3 {
                return Err(ParseMessageError::ParseDaemonMessageError);
            }
            let limit = fields[0].clone().parse().unwrap_or(0);
            let remaining = fields[1].clone().parse().unwrap_or(0);
            let reset = fields[2].clone().parse().unwrap_or(0);
            Ok(DaemonMessage::RateLimit { limit,
                                          remaining,
                                          reset })
        } else if let Some(msg) = s.strip_prefix("d:sectors:") {
            let sectors = msg.split('\n')
                             .filter_map(|l| l.parse::<structs::SectorGroup>().ok())
                             .collect();
            Ok(DaemonMessage::SectorGroups { sectors })
        } else if let Some(msg) = s.strip_prefix("d:pw:") {
            let fields: Vec<String> = msg.split(':').map(|s| s.to_string()).collect();
            if fields.len() < 5 {
                return Err(ParseMessageError::ParseDaemonMessageError);
            }
            let login: String = fields[0].clone();
            let home: String = fields[3].clone();
            let sh: String = fields[4].clone();
            match (fields[1].parse::<u64>(), fields[2].parse::<u64>()) {
                (Ok(uid), Ok(gid)) => Ok(DaemonMessage::Pw { login,
                                                             uid,
                                                             gid,
                                                             home,
                                                             sh }),
                _ => Err(ParseMessageError::ParseDaemonMessageError),
            }
        } else if let Some(msg) = s.strip_prefix("d:sp:") {
            let fields: Vec<String> = msg.split(':').map(|s| s.to_string()).collect();
            if fields.len() < 2 {
                return Err(ParseMessageError::ParseDaemonMessageError);
            }
            Ok(DaemonMessage::Sp { login: fields[0].clone(),
                                   pass: fields[1].clone() })
        } else if let Some(msg) = s.strip_prefix("d:gr:") {
            match msg.parse::<structs::SectorGroup>() {
                Ok(sector) => Ok(DaemonMessage::Gr { sector }),
                _ => Err(ParseMessageError::ParseDaemonMessageError),
            }
        } else {
            Err(ParseMessageError::ParseDaemonMessageError)
        }
    }
}
<commit_msg>Simplify<commit_after>use crate::error::ParseMessageError;
use crate::structs;
use std::fmt;
use std::str::FromStr;

#[derive(Debug)]
pub enum Pw {
    Uid(u64),
    Nam(String),
    Ent(Ent),
}

#[derive(Debug)]
pub enum Sp {
    Nam(String),
    Ent(Ent),
}

#[derive(Debug)]
pub enum Gr {
    Gid(u64),
    Nam(String),
    Ent(Ent),
}

#[derive(Debug)]
pub enum Ent {
    Set(u32),
    Get(u32),
    End(u32),
}

pub struct DividedMessage {
    pub cont: bool,
    pub message: String,
}

impl DividedMessage {
    #[allow(dead_code)]
    pub fn new(msg: &str, size: usize) -> Vec<Self> {
        let mut msgs = vec![];
        let mut idx = 0;
        while idx + size < msg.len() {
            msgs.push(Self { cont: true,
                             message: msg[idx..idx + size].to_owned() });
            idx += size
        }
        msgs.push(Self { cont: false,
                         message: msg[idx..msg.len()].to_owned() });
        msgs
    }
}

impl fmt::Display for DividedMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}", if self.cont { 1 } else { 0 }, self.message)
    }
}

impl FromStr for DividedMessage {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("0:") {
            Ok(Self { cont: false,
                      message: msg.to_owned() })
        } else if let Some(msg) = s.strip_prefix("1:") {
            Ok(Self { cont: true,
                      message: msg.to_owned() })
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

#[derive(Debug)]
pub enum ClientMessage {
    Cont,
    Key { user: String },
    Pam { user: String },
    CleanUp,
    RateLimit,
    SectorGroups,
    Pw(Pw),
    Sp(Sp),
    Gr(Gr),
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum DaemonMessage {
    Success,
    Error {
        message: String,
    },
    Key {
        keys: String,
    },
    Pam {
        result: bool,
    },
    RateLimit {
        limit: usize,
        remaining: usize,
        reset: usize,
    },
    SectorGroups {
        sectors: Vec<structs::SectorGroup>,
    },
    Pw {
        login: String,
        uid: u64,
        gid: u64,
        home: String,
        sh: String,
    },
    Sp {
        login: String,
        pass: String,
    },
    Gr {
        sector: structs::SectorGroup,
    },
}

impl fmt::Display for Ent {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Ent::Set(pid) => write!(f, "set|{}", pid),
            Ent::Get(pid) => write!(f, "get|{}", pid),
            Ent::End(pid) => write!(f, "end|{}", pid),
        }
    }
}

impl fmt::Display for Pw {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Pw::Uid(uid) => write!(f, "uid={}", uid),
            Pw::Nam(name) => write!(f, "name={}", name),
            Pw::Ent(ent) => write!(f, "ent={}", ent),
        }
    }
}

impl fmt::Display for Sp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Sp::Nam(name) => write!(f, "name={}", name),
            Sp::Ent(ent) => write!(f, "ent={}", ent),
        }
    }
}

impl fmt::Display for Gr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Gr::Gid(gid) => write!(f, "gid={}", gid),
            Gr::Nam(name) => write!(f, "name={}", name),
            Gr::Ent(ent) => write!(f, "ent={}", ent),
        }
    }
}

impl fmt::Display for ClientMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ClientMessage::Cont => write!(f, "c:cont"),
            ClientMessage::Key { user } => write!(f, "c:key:{}", user),
            ClientMessage::Pam { user } => write!(f, "c:pam:{}", user),
            ClientMessage::CleanUp => write!(f, "c:cleanup"),
            ClientMessage::RateLimit => write!(f, "c:ratelimit"),
            ClientMessage::SectorGroups => write!(f, "c:sectors"),
            ClientMessage::Pw(pw) => write!(f, "c:pw:{}", pw),
            ClientMessage::Sp(sp) => write!(f, "c:sp:{}", sp),
            ClientMessage::Gr(gr) => write!(f, "c:gr:{}", gr),
        }
    }
}

impl fmt::Display for DaemonMessage {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            DaemonMessage::Error { message } => write!(f, "d:error:{}", message),
            DaemonMessage::Success => write!(f, "d:success"),
            DaemonMessage::Key { keys } => write!(f, "d:key:{}", keys),
            DaemonMessage::Pam { result } => write!(f, "d:pam:{}", result),
            DaemonMessage::RateLimit { limit,
                                       remaining,
                                       reset, } => write!(f, "d:ratelimit:{}:{}:{}", limit, remaining, reset),
            DaemonMessage::SectorGroups { sectors } => {
                let ss: Vec<String> = sectors.iter().map(|s| s.to_string()).collect();
                write!(f, "d:sectors:{}", ss.join("\n"))
            }
            DaemonMessage::Pw { login,
                                uid,
                                gid,
                                home,
                                sh, } => write!(f, "d:pw:{}:{}:{}:{}:{}", login, uid, gid, home, sh),
            DaemonMessage::Sp { login, pass } => write!(f, "d:sp:{}:{}", login, pass),
            DaemonMessage::Gr { sector } => write!(f, "d:gr:{}", sector),
        }
    }
}

impl FromStr for Ent {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("set|") {
            Ok(Ent::Set(msg.parse::<u32>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("get|") {
            Ok(Ent::Get(msg.parse::<u32>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("end|") {
            Ok(Ent::End(msg.parse::<u32>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for Pw {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("uid=") {
            Ok(Pw::Uid(msg.parse::<u64>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("name=") {
            Ok(Pw::Nam(String::from(msg)))
        } else if let Some(msg) = s.strip_prefix("ent=") {
            Ok(Pw::Ent(msg.parse::<Ent>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for Sp {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("name=") {
            Ok(Sp::Nam(String::from(msg)))
        } else if let Some(msg) = s.strip_prefix("ent=") {
            Ok(Sp::Ent(msg.parse::<Ent>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for Gr {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("gid=") {
            Ok(Gr::Gid(msg.parse::<u64>().unwrap()))
        } else if let Some(msg) = s.strip_prefix("name=") {
            Ok(Gr::Nam(String::from(msg)))
        } else if let Some(msg) = s.strip_prefix("ent=") {
            Ok(Gr::Ent(msg.parse::<Ent>().unwrap()))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for ClientMessage {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s == "c:cont" {
            Ok(ClientMessage::Cont)
        } else if let Some(msg) = s.strip_prefix("c:key:") {
            Ok(ClientMessage::Key { user: String::from(msg) })
        } else if let Some(msg) = s.strip_prefix("c:pam:") {
            Ok(ClientMessage::Pam { user: String::from(msg) })
        } else if s == "c:cleanup" {
            Ok(ClientMessage::CleanUp)
        } else if s == "c:ratelimit" {
            Ok(ClientMessage::RateLimit)
        } else if s == "c:sectors" {
            Ok(ClientMessage::SectorGroups)
        } else if let Some(msg) = s.strip_prefix("c:pw:") {
            Ok(ClientMessage::Pw(msg.parse::<Pw>()?))
        } else if let Some(msg) = s.strip_prefix("c:sp:") {
            Ok(ClientMessage::Sp(msg.parse::<Sp>()?))
        } else if let Some(msg) = s.strip_prefix("c:gr:") {
            Ok(ClientMessage::Gr(msg.parse::<Gr>()?))
        } else {
            Err(ParseMessageError::ParseClientMessageError)
        }
    }
}

impl FromStr for DaemonMessage {
    type Err = ParseMessageError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if let Some(msg) = s.strip_prefix("d:key:") {
            Ok(DaemonMessage::Key { keys: String::from(msg) })
        } else if let Some(msg) = s.strip_prefix("d:pam:") {
            Ok(DaemonMessage::Pam { result: FromStr::from_str(msg).unwrap_or(false) })
        } else if s == "d:success" {
            Ok(DaemonMessage::Success)
        } else if let Some(msg) = s.strip_prefix("d:ratelimit:") {
            let fields: Vec<String> = msg.split(':').map(|s| s.to_string()).collect();
            if fields.len() < 3 {
                return Err(ParseMessageError::ParseDaemonMessageError);
            }
            let limit = fields[0].clone().parse().unwrap_or(0);
            let remaining = fields[1].clone().parse().unwrap_or(0);
            let reset = fields[2].clone().parse().unwrap_or(0);
            Ok(DaemonMessage::RateLimit { limit,
                                          remaining,
                                          reset })
        } else if let Some(msg) = s.strip_prefix("d:sectors:") {
            let sectors = msg.split('\n')
                             .filter_map(|l| l.parse::<structs::SectorGroup>().ok())
                             .collect();
            Ok(DaemonMessage::SectorGroups { sectors })
        } else if let Some(msg) = s.strip_prefix("d:pw:") {
            let fields: Vec<String> = msg.split(':').map(|s| s.to_string()).collect();
            if fields.len() < 5 {
                return Err(ParseMessageError::ParseDaemonMessageError);
            }
            let login: String = fields[0].clone();
            let home: String = fields[3].clone();
            let sh: String = fields[4].clone();
            match (fields[1].parse::<u64>(), fields[2].parse::<u64>()) {
                (Ok(uid), Ok(gid)) => Ok(DaemonMessage::Pw { login,
                                                             uid,
                                                             gid,
                                                             home,
                                                             sh }),
                _ => Err(ParseMessageError::ParseDaemonMessageError),
            }
        } else if let Some(msg) = s.strip_prefix("d:sp:") {
            let fields: Vec<String> = msg.split(':').map(|s| s.to_string()).collect();
            if fields.len() < 2 {
                return Err(ParseMessageError::ParseDaemonMessageError);
            }
            Ok(DaemonMessage::Sp { login: fields[0].clone(),
                                   pass: fields[1].clone() })
        } else if let Some(msg) = s.strip_prefix("d:gr:") {
            match msg.parse::<structs::SectorGroup>() {
                Ok(sector) => Ok(DaemonMessage::Gr { sector }),
                _ => Err(ParseMessageError::ParseDaemonMessageError),
            }
        } else {
            Err(ParseMessageError::ParseDaemonMessageError)
        }
    }
}
<|endoftext|>