<commit_before>use std::sync::Arc;
use std::collections::{HashMap, VecDeque};
use std::{fs, fmt, path};
use std::io::{self, Seek, SeekFrom, Write, Read};
use std::path::PathBuf;
use torrent::Info;
use util::hash_to_id;
use ring::digest;
use amy;
use {handle, CONFIG};

const POLL_INT_MS: usize = 1000;

pub struct Disk {
    poll: amy::Poller,
    ch: handle::Handle<Request, Response>,
    files: FileCache,
    active: VecDeque<Request>,
}

struct FileCache {
    files: HashMap<path::PathBuf, fs::File>,
}

pub enum Request {
    Write {
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    },
    Read {
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        context: Ctx,
        path: Option<String>,
    },
    Serialize {
        tid: usize,
        data: Vec<u8>,
        hash: [u8; 20],
    },
    Delete {
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    },

    Validate {
        tid: usize,
        info: Arc<Info>,
        path: Option<String>,
        idx: u32,
        invalid: Vec<u32>,
    },
    Shutdown,
}

pub struct Ctx {
    pub pid: usize,
    pub tid: usize,
    pub idx: u32,
    pub begin: u32,
    pub length: u32,
}

impl Ctx {
    pub fn new(pid: usize, tid: usize, idx: u32, begin: u32, length: u32) -> Ctx {
        Ctx {
            pid,
            tid,
            idx,
            begin,
            length,
        }
    }
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache { files: HashMap::new() }
    }

    pub fn get_file<F: FnOnce(&mut fs::File) -> io::Result<()>>(
        &mut self,
        path: &path::Path,
        f: F,
    ) -> io::Result<()> {
        if self.files.contains_key(path) {
            f(self.files.get_mut(path).unwrap())?;
        } else {
            // TODO: LRU maybe?
            if self.files.len() >= CONFIG.net.max_open_files {
                let removal = self.files.iter().map(|(id, _)| id.clone()).next().unwrap();
                self.files.remove(&removal);
            }
            fs::create_dir_all(path.parent().unwrap())?;
            let mut file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;
            f(&mut file)?;
            self.files.insert(path.to_path_buf(), file);
        }
        Ok(())
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        self.files.remove(path);
    }
}

impl Request {
    pub fn write(
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Write {
            tid,
            data,
            locations,
            path,
        }
    }

    pub fn read(
        context: Ctx,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Read {
            context,
            data,
            locations,
            path,
        }
    }

    pub fn serialize(tid: usize, data: Vec<u8>, hash: [u8; 20]) -> Request {
        Request::Serialize { tid, data, hash }
    }

    pub fn validate(tid: usize, info: Arc<Info>, path: Option<String>) -> Request {
        Request::Validate {
            tid,
            info,
            path,
            idx: 0,
            invalid: Vec::new(),
        }
    }

    pub fn delete(
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    ) -> Request {
        Request::Delete {
            tid,
            hash,
            files,
            path,
        }
    }

    pub fn shutdown() -> Request {
        Request::Shutdown
    }

    fn execute(self, fc: &mut FileCache) -> io::Result<Result<Response, Option<Self>>> {
        let sd = &CONFIG.disk.session;
        let dd = &CONFIG.disk.directory;
        match self {
            Request::Write {
                data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.write_all(&data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
            }
            Request::Read {
                context,
                mut data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.read_exact(&mut data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
                let data = Arc::new(data);
                return Ok(Ok(Response::read(context, data)));
            }
            Request::Serialize { data, hash, .. } => {
                let mut pb = path::PathBuf::from(sd);
                pb.push(hash_to_id(&hash));
                let mut f = fs::OpenOptions::new().write(true).create(true).open(&pb)?;
                f.write_all(&data)?;
            }
            Request::Delete { hash, files, path, .. } => {
                let mut spb = path::PathBuf::from(sd);
                spb.push(hash_to_id(&hash));
                fs::remove_file(spb)?;

                for file in files {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&file);
                    fc.remove_file(&pb);
                }
            }
            Request::Validate {
                tid,
                info,
                path,
                mut idx,
                mut invalid,
            } => {
                let mut buf = vec![0u8; info.piece_len as usize];
                let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                let mut cf = pb.clone();

                let mut f = fs::OpenOptions::new().read(true).open(&pb);

                let mut max = 0;

                while idx < info.pieces() && max < 10 {
                    let mut valid = true;
                    let mut ctx = digest::Context::new(&digest::SHA1);
                    let locs = info.piece_disk_locs(idx);
                    let mut pos = 0;
                    for loc in locs {
                        if loc.file != cf {
                            pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                            pb.push(&loc.file);
                            f = fs::OpenOptions::new().read(true).open(&pb);
                            cf = loc.file.clone();
                        }
                        // Because this is pausable/resumable, we need to seek to the proper
                        // file position.
                        f.as_mut()
                            .map(|file| file.seek(SeekFrom::Start(loc.offset)))
                            .ok();
                        if let Ok(Ok(amnt)) = f.as_mut().map(|file| file.read(&mut buf[pos..])) {
                            ctx.update(&buf[pos..pos + amnt]);
                            pos += amnt;
                        } else {
                            valid = false;
                        }
                    }
                    let digest = ctx.finish();
                    if !valid || digest.as_ref() != &info.hashes[idx as usize][..] {
                        invalid.push(idx);
                    }

                    max += 1;
                    idx += 1;
                }
                if idx == info.pieces() {
                    return Ok(Ok(Response::validation_complete(tid, invalid)));
                } else {
                    return Ok(Err(Some(Request::Validate {
                        tid,
                        info,
                        path,
                        idx,
                        invalid,
                    })));
                }
            }
            Request::Shutdown => unreachable!(),
        }
        Ok(Err(None))
    }

    pub fn tid(&self) -> usize {
        match *self {
            Request::Serialize { tid, .. } |
            Request::Validate { tid, .. } |
            Request::Delete { tid, .. } |
            Request::Write { tid, .. } => tid,
            Request::Read { ref context, .. } => context.tid,
            Request::Shutdown => unreachable!(),
        }
    }
}

impl fmt::Debug for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Request")
    }
}

pub struct Location {
    pub file: PathBuf,
    pub offset: u64,
    pub start: usize,
    pub end: usize,
}

impl Location {
    pub fn new(file: PathBuf, offset: u64, start: u64, end: u64) -> Location {
        Location {
            file,
            offset,
            start: start as usize,
            end: end as usize,
        }
    }
}

pub enum Response {
    Read {
        context: Ctx,
        data: Arc<Box<[u8; 16_384]>>,
    },
    ValidationComplete { tid: usize, invalid: Vec<u32> },
    Error { tid: usize, err: io::Error },
}

impl Response {
    pub fn read(context: Ctx, data: Arc<Box<[u8; 16_384]>>) -> Response {
        Response::Read { context, data }
    }

    pub fn error(tid: usize, err: io::Error) -> Response {
        Response::Error { tid, err }
    }

    pub fn validation_complete(tid: usize, invalid: Vec<u32>) -> Response {
        Response::ValidationComplete { tid, invalid }
    }

    pub fn tid(&self) -> usize {
        match *self {
            Response::Read { ref context, .. } => context.tid,
            Response::ValidationComplete { tid, .. } |
            Response::Error { tid, .. } => tid,
        }
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Response")
    }
}

impl Disk {
    pub fn new(poll: amy::Poller, ch: handle::Handle<Request, Response>) -> Disk {
        Disk {
            poll,
            ch,
            files: FileCache::new(),
            active: VecDeque::new(),
        }
    }

    pub fn run(&mut self) {
        let sd = &CONFIG.disk.session;
        fs::create_dir_all(sd).unwrap();

        loop {
            match self.poll.wait(POLL_INT_MS) {
                Ok(_) => {
                    if self.handle_events() {
                        break;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
            if !self.active.is_empty() {
                if self.handle_active() {
                    break;
                }
            }
        }
    }

    fn handle_active(&mut self) -> bool {
        while let Some(j) = self.active.pop_front() {
            let tid = j.tid();
            match j.execute(&mut self.files) {
                Ok(Ok(r)) => {
                    self.ch.send(r).ok();
                }
                Ok(Err(Some(s))) => {
                    self.active.push_front(s);
                }
                Ok(Err(None)) => {}
                Err(e) => {
                    self.ch.send(Response::error(tid, e)).ok();
                }
            }
            match self.poll.wait(10) {
                Ok(_) => {
                    if self.handle_events() {
                        return true;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
        }
        false
    }

    pub fn handle_events(&mut self) -> bool {
        loop {
            match self.ch.recv() {
                Ok(Request::Shutdown) => {
                    return true;
                }
                Ok(r) => {
                    trace!("Handling disk job!");
                    let tid = r.tid();
                    match r.execute(&mut self.files) {
                        Ok(Ok(r)) => {
                            self.ch.send(r).ok();
                        }
                        Ok(Err(Some(s))) => {
                            self.active.push_back(s);
                        }
                        Ok(Err(None)) => {}
                        Err(e) => {
                            self.ch.send(Response::error(tid, e)).ok();
                        }
                    }
                }
                _ => break,
            }
        }
        false
    }
}

pub fn start(creg: &mut amy::Registrar) -> io::Result<handle::Handle<Response, Request>> {
    let poll = amy::Poller::new()?;
    let mut reg = poll.get_registrar()?;
    let (ch, dh) = handle::Handle::new(creg, &mut reg)?;
    dh.run("disk", move |h| Disk::new(poll, h).run());
    Ok(ch)
}
<commit_msg>Cleanup disk job result type<commit_after>use std::sync::Arc;
use std::collections::{HashMap, VecDeque};
use std::{fs, fmt, path};
use std::io::{self, Seek, SeekFrom, Write, Read};
use std::path::PathBuf;
use torrent::Info;
use util::hash_to_id;
use ring::digest;
use amy;
use {handle, CONFIG};

const POLL_INT_MS: usize = 1000;

pub struct Disk {
    poll: amy::Poller,
    ch: handle::Handle<Request, Response>,
    files: FileCache,
    active: VecDeque<Request>,
}

struct FileCache {
    files: HashMap<path::PathBuf, fs::File>,
}

pub enum Request {
    Write {
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    },
    Read {
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        context: Ctx,
        path: Option<String>,
    },
    Serialize {
        tid: usize,
        data: Vec<u8>,
        hash: [u8; 20],
    },
    Delete {
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    },

    Validate {
        tid: usize,
        info: Arc<Info>,
        path: Option<String>,
        idx: u32,
        invalid: Vec<u32>,
    },
    Shutdown,
}

enum JobRes {
    Resp(Response),
    Done,
    Paused(Request),
}

pub struct Ctx {
    pub pid: usize,
    pub tid: usize,
    pub idx: u32,
    pub begin: u32,
    pub length: u32,
}

impl Ctx {
    pub fn new(pid: usize, tid: usize, idx: u32, begin: u32, length: u32) -> Ctx {
        Ctx {
            pid,
            tid,
            idx,
            begin,
            length,
        }
    }
}

impl FileCache {
    pub fn new() -> FileCache {
        FileCache { files: HashMap::new() }
    }

    pub fn get_file<F: FnOnce(&mut fs::File) -> io::Result<()>>(
        &mut self,
        path: &path::Path,
        f: F,
    ) -> io::Result<()> {
        if self.files.contains_key(path) {
            f(self.files.get_mut(path).unwrap())?;
        } else {
            // TODO: LRU maybe?
            if self.files.len() >= CONFIG.net.max_open_files {
                let removal = self.files.iter().map(|(id, _)| id.clone()).next().unwrap();
                self.files.remove(&removal);
            }
            fs::create_dir_all(path.parent().unwrap())?;
            let mut file = fs::OpenOptions::new()
                .write(true)
                .create(true)
                .read(true)
                .open(path)?;
            f(&mut file)?;
            self.files.insert(path.to_path_buf(), file);
        }
        Ok(())
    }

    pub fn remove_file(&mut self, path: &path::Path) {
        self.files.remove(path);
    }
}

impl Request {
    pub fn write(
        tid: usize,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Write {
            tid,
            data,
            locations,
            path,
        }
    }

    pub fn read(
        context: Ctx,
        data: Box<[u8; 16_384]>,
        locations: Vec<Location>,
        path: Option<String>,
    ) -> Request {
        Request::Read {
            context,
            data,
            locations,
            path,
        }
    }

    pub fn serialize(tid: usize, data: Vec<u8>, hash: [u8; 20]) -> Request {
        Request::Serialize { tid, data, hash }
    }

    pub fn validate(tid: usize, info: Arc<Info>, path: Option<String>) -> Request {
        Request::Validate {
            tid,
            info,
            path,
            idx: 0,
            invalid: Vec::new(),
        }
    }

    pub fn delete(
        tid: usize,
        hash: [u8; 20],
        files: Vec<PathBuf>,
        path: Option<String>,
    ) -> Request {
        Request::Delete {
            tid,
            hash,
            files,
            path,
        }
    }

    pub fn shutdown() -> Request {
        Request::Shutdown
    }

    fn execute(self, fc: &mut FileCache) -> io::Result<JobRes> {
        let sd = &CONFIG.disk.session;
        let dd = &CONFIG.disk.directory;
        match self {
            Request::Write {
                data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.write_all(&data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
            }
            Request::Read {
                context,
                mut data,
                locations,
                path,
                ..
            } => {
                for loc in locations {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&loc.file);
                    fc.get_file(&pb, |f| {
                        f.seek(SeekFrom::Start(loc.offset))?;
                        f.read_exact(&mut data[loc.start..loc.end])?;
                        Ok(())
                    })?;
                }
                let data = Arc::new(data);
                return Ok(JobRes::Resp(Response::read(context, data)));
            }
            Request::Serialize { data, hash, .. } => {
                let mut pb = path::PathBuf::from(sd);
                pb.push(hash_to_id(&hash));
                let mut f = fs::OpenOptions::new().write(true).create(true).open(&pb)?;
                f.write_all(&data)?;
            }
            Request::Delete { hash, files, path, .. } => {
                let mut spb = path::PathBuf::from(sd);
                spb.push(hash_to_id(&hash));
                fs::remove_file(spb)?;

                for file in files {
                    let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                    pb.push(&file);
                    fc.remove_file(&pb);
                }
            }
            Request::Validate {
                tid,
                info,
                path,
                mut idx,
                mut invalid,
            } => {
                let mut buf = vec![0u8; info.piece_len as usize];
                let mut pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                let mut cf = pb.clone();

                let mut f = fs::OpenOptions::new().read(true).open(&pb);

                let mut max = 0;

                while idx < info.pieces() && max < 10 {
                    let mut valid = true;
                    let mut ctx = digest::Context::new(&digest::SHA1);
                    let locs = info.piece_disk_locs(idx);
                    let mut pos = 0;
                    for loc in locs {
                        if loc.file != cf {
                            pb = path::PathBuf::from(path.as_ref().unwrap_or(dd));
                            pb.push(&loc.file);
                            f = fs::OpenOptions::new().read(true).open(&pb);
                            cf = loc.file.clone();
                        }
                        // Because this is pausable/resumable, we need to seek to the proper
                        // file position.
                        f.as_mut()
                            .map(|file| file.seek(SeekFrom::Start(loc.offset)))
                            .ok();
                        if let Ok(Ok(amnt)) = f.as_mut().map(|file| file.read(&mut buf[pos..])) {
                            ctx.update(&buf[pos..pos + amnt]);
                            pos += amnt;
                        } else {
                            valid = false;
                        }
                    }
                    let digest = ctx.finish();
                    if !valid || digest.as_ref() != &info.hashes[idx as usize][..] {
                        invalid.push(idx);
                    }

                    max += 1;
                    idx += 1;
                }
                if idx == info.pieces() {
                    return Ok(JobRes::Resp(Response::validation_complete(tid, invalid)));
                } else {
                    return Ok(JobRes::Paused(Request::Validate {
                        tid,
                        info,
                        path,
                        idx,
                        invalid,
                    }));
                }
            }
            Request::Shutdown => unreachable!(),
        }
        Ok(JobRes::Done)
    }

    pub fn tid(&self) -> usize {
        match *self {
            Request::Serialize { tid, .. } |
            Request::Validate { tid, .. } |
            Request::Delete { tid, .. } |
            Request::Write { tid, .. } => tid,
            Request::Read { ref context, .. } => context.tid,
            Request::Shutdown => unreachable!(),
        }
    }
}

impl fmt::Debug for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Request")
    }
}

pub struct Location {
    pub file: PathBuf,
    pub offset: u64,
    pub start: usize,
    pub end: usize,
}

impl Location {
    pub fn new(file: PathBuf, offset: u64, start: u64, end: u64) -> Location {
        Location {
            file,
            offset,
            start: start as usize,
            end: end as usize,
        }
    }
}

pub enum Response {
    Read {
        context: Ctx,
        data: Arc<Box<[u8; 16_384]>>,
    },
    ValidationComplete { tid: usize, invalid: Vec<u32> },
    Error { tid: usize, err: io::Error },
}

impl Response {
    pub fn read(context: Ctx, data: Arc<Box<[u8; 16_384]>>) -> Response {
        Response::Read { context, data }
    }

    pub fn error(tid: usize, err: io::Error) -> Response {
        Response::Error { tid, err }
    }

    pub fn validation_complete(tid: usize, invalid: Vec<u32>) -> Response {
        Response::ValidationComplete { tid, invalid }
    }

    pub fn tid(&self) -> usize {
        match *self {
            Response::Read { ref context, .. } => context.tid,
            Response::ValidationComplete { tid, .. } |
            Response::Error { tid, .. } => tid,
        }
    }
}

impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "disk::Response")
    }
}

impl Disk {
    pub fn new(poll: amy::Poller, ch: handle::Handle<Request, Response>) -> Disk {
        Disk {
            poll,
            ch,
            files: FileCache::new(),
            active: VecDeque::new(),
        }
    }

    pub fn run(&mut self) {
        let sd = &CONFIG.disk.session;
        fs::create_dir_all(sd).unwrap();

        loop {
            match self.poll.wait(POLL_INT_MS) {
                Ok(_) => {
                    if self.handle_events() {
                        break;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
            if !self.active.is_empty() {
                if self.handle_active() {
                    break;
                }
            }
        }
    }

    fn handle_active(&mut self) -> bool {
        while let Some(j) = self.active.pop_front() {
            let tid = j.tid();
            match j.execute(&mut self.files) {
                Ok(JobRes::Resp(r)) => {
                    self.ch.send(r).ok();
                }
                Ok(JobRes::Paused(s)) => {
                    self.active.push_front(s);
                }
                Ok(JobRes::Done) => {}
                Err(e) => {
                    self.ch.send(Response::error(tid, e)).ok();
                }
            }
            match self.poll.wait(0) {
                Ok(_) => {
                    if self.handle_events() {
                        return true;
                    }
                }
                Err(e) => {
                    error!("Failed to poll for events: {:?}", e);
                }
            }
        }
        false
    }

    pub fn handle_events(&mut self) -> bool {
        loop {
            match self.ch.recv() {
                Ok(Request::Shutdown) => {
                    return true;
                }
                Ok(r) => {
                    trace!("Handling disk job!");
                    let tid = r.tid();
                    match r.execute(&mut self.files) {
                        Ok(JobRes::Resp(r)) => {
                            self.ch.send(r).ok();
                        }
                        Ok(JobRes::Paused(s)) => {
                            self.active.push_back(s);
                        }
                        Ok(JobRes::Done) => {}
                        Err(e) => {
                            self.ch.send(Response::error(tid, e)).ok();
                        }
                    }
                }
                _ => break,
            }
        }
        false
    }
}

pub fn start(creg: &mut amy::Registrar) -> io::Result<handle::Handle<Response, Request>> {
    let poll = amy::Poller::new()?;
    let mut reg = poll.get_registrar()?;
    let (ch, dh) = handle::Handle::new(creg, &mut reg)?;
    dh.run("disk", move |h| Disk::new(poll, h).run());
    Ok(ch)
}
<|endoftext|>


<commit_before>extern crate proc_macro;

use proc_macro::TokenStream;
use syn::{parse_macro_input, File};

#[proc_macro]
pub fn r#mod(input: TokenStream) -> TokenStream {
    parse_macro_input!(input as File);
    "fn main() {}".parse().unwrap()
}
<commit_msg>Debug print the dev syntax tree<commit_after>extern crate proc_macro;

use proc_macro::TokenStream;
use syn::{parse_macro_input, File};

#[proc_macro]
pub fn r#mod(input: TokenStream) -> TokenStream {
    let file = parse_macro_input!(input as File);
    println!("{:#?}", file);
    "fn main() {}".parse().unwrap()
}
<|endoftext|>


<commit_before>//! Main evaluator loop and setting up the initial stack frame.

use std::convert::TryFrom;
use std::ffi::OsStr;

use rand::rngs::StdRng;
use rand::SeedableRng;

use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, layout::LayoutCx, TyCtxt};
use rustc_target::abi::LayoutOf;

use crate::*;

/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
    /// Determine if validity checking is enabled.
    pub validate: bool,
    /// Determines if Stacked Borrows is enabled.
    pub stacked_borrows: bool,
    /// Determines if alignment checking is enabled.
    pub check_alignment: bool,
    /// Determines if communication with the host environment is enabled.
    pub communicate: bool,
    /// Determines if memory leaks should be ignored.
    pub ignore_leaks: bool,
    /// Environment variables that should always be isolated from the host.
    pub excluded_env_vars: Vec<String>,
    /// Command-line arguments passed to the interpreted program.
    pub args: Vec<String>,
    /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
    pub seed: Option<u64>,
    /// The stacked borrow id to report about
    pub tracked_pointer_tag: Option<PtrId>,
    /// The allocation id to report about.
    pub tracked_alloc_id: Option<AllocId>,
}

impl Default for MiriConfig {
    fn default() -> MiriConfig {
        MiriConfig {
            validate: true,
            stacked_borrows: true,
            check_alignment: true,
            communicate: false,
            ignore_leaks: false,
            excluded_env_vars: vec![],
            args: vec![],
            seed: None,
            tracked_pointer_tag: None,
            tracked_alloc_id: None,
        }
    }
}

/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
/// the location where the return value of the `start` lang item will be
/// written to.
/// Public because this is also used by `priroda`.
pub fn create_ecx<'mir, 'tcx: 'mir>(
    tcx: TyCtxt<'tcx>,
    main_id: DefId,
    config: MiriConfig,
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Tag>)> {
    let tcx_at = tcx.at(rustc_span::source_map::DUMMY_SP);
    let param_env = ty::ParamEnv::reveal_all();
    let layout_cx = LayoutCx { tcx, param_env };
    let mut ecx = InterpCx::new(
        tcx_at,
        param_env,
        Evaluator::new(config.communicate, config.validate, layout_cx),
        MemoryExtra::new(
            StdRng::seed_from_u64(config.seed.unwrap_or(0)),
            config.stacked_borrows,
            config.tracked_pointer_tag,
            config.tracked_alloc_id,
            config.check_alignment,
        ),
    );
    // Complete initialization.
    EnvVars::init(&mut ecx, config.excluded_env_vars)?;
    MemoryExtra::init_extern_statics(&mut ecx)?;

    // Setup first stack-frame
    let main_instance = ty::Instance::mono(tcx, main_id);
    let main_mir = ecx.load_mir(main_instance.def, None)?;
    if main_mir.arg_count != 0 {
        bug!("main function must not take any arguments");
    }

    let start_id = tcx.lang_items().start_fn().unwrap();
    let main_ret_ty = tcx.fn_sig(main_id).output();
    let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
    let start_instance = ty::Instance::resolve(
        tcx,
        ty::ParamEnv::reveal_all(),
        start_id,
        tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(main_ret_ty))),
    )
    .unwrap()
    .unwrap();

    // First argument: pointer to `main()`.
    let main_ptr = ecx.memory.create_fn_alloc(FnVal::Instance(main_instance));
    // Second argument (argc): length of `config.args`.
    let argc = Scalar::from_machine_usize(u64::try_from(config.args.len()).unwrap(), &ecx);
    // Third argument (`argv`): created from `config.args`.
    let argv = {
        // Put each argument in memory, collect pointers.
        let mut argvs = Vec::<Scalar<Tag>>::new();
        for arg in config.args.iter() {
            // Make space for `0` terminator.
            let size = u64::try_from(arg.len()).unwrap().checked_add(1).unwrap();
            let arg_type = tcx.mk_array(tcx.types.u8, size);
            let arg_place = ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Machine.into());
            ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?;
            argvs.push(arg_place.ptr);
        }
        // Make an array with all these pointers, in the Miri memory.
        let argvs_layout =
            ecx.layout_of(tcx.mk_array(tcx.mk_imm_ptr(tcx.types.u8), u64::try_from(argvs.len()).unwrap()))?;
        let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into());
        for (idx, arg) in argvs.into_iter().enumerate() {
            let place = ecx.mplace_field(argvs_place, idx)?;
            ecx.write_scalar(arg, place.into())?;
        }
        ecx.memory.mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
        // A pointer to that place is the 3rd argument for main.
        let argv = argvs_place.ptr;
        // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
        {
            let argc_place =
                ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into());
            ecx.write_scalar(argc, argc_place.into())?;
            ecx.machine.argc = Some(argc_place.ptr);

            let argv_place = ecx.allocate(
                ecx.layout_of(tcx.mk_imm_ptr(tcx.types.unit))?,
                MiriMemoryKind::Machine.into(),
            );
            ecx.write_scalar(argv, argv_place.into())?;
            ecx.machine.argv = Some(argv_place.ptr);
        }
        // Store command line as UTF-16 for Windows `GetCommandLineW`.
        {
            // Construct a command string with all the aguments.
            let mut cmd = String::new();
            for arg in config.args.iter() {
                if !cmd.is_empty() {
                    cmd.push(' ');
                }
                cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
            }
            // Don't forget `0` terminator.
            cmd.push(std::char::from_u32(0).unwrap());

            let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
            let cmd_type = tcx.mk_array(tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap());
            let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into());
            ecx.machine.cmd_line = Some(cmd_place.ptr);
            // Store the UTF-16 string. We just allocated so we know the bounds are fine.
            for (idx, &c) in cmd_utf16.iter().enumerate() {
                let place = ecx.mplace_field(cmd_place, idx)?;
                ecx.write_scalar(Scalar::from_u16(c), place.into())?;
            }
        }
        argv
    };

    // Return place (in static memory so that it does not count as leak).
    let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into());
    // Call start function.
    ecx.call_function(
        start_instance,
        &[main_ptr.into(), argc.into(), argv.into()],
        Some(ret_place.into()),
        StackPopCleanup::None { cleanup: true },
    )?;

    // Set the last_error to 0
    let errno_layout = ecx.machine.layouts.u32;
    let errno_place = ecx.allocate(errno_layout, MiriMemoryKind::Machine.into());
    ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?;
    ecx.machine.last_error = Some(errno_place);

    Ok((ecx, ret_place))
}

/// Evaluates the main function specified by `main_id`.
/// Returns `Some(return_code)` if program executed completed.
/// Returns `None` if an evaluation error occured.
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
    // FIXME: on Windows, we ignore leaks (https://github.com/rust-lang/miri/issues/1302).
    let ignore_leaks = config.ignore_leaks || tcx.sess.target.target.target_os == "windows";

    let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
        Ok(v) => v,
        Err(mut err) => {
            err.print_backtrace();
            panic!("Miri initialization error: {}", err.kind)
        }
    };

    // Perform the main execution.
    let res: InterpResult<'_, i64> = (|| {
        // Main loop.
        while ecx.schedule()? {
            assert!(ecx.step()?, "Bug: a terminated thread was scheduled for execution.");
            ecx.process_diagnostics();
        }
        // Read the return code pointer *before* we run TLS destructors, to assert
        // that it was written to by the time that `start` lang item returned.
        let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
        // Global destructors.
        ecx.run_tls_dtors()?;
        Ok(return_code)
    })();

    // Machine cleanup.
    EnvVars::cleanup(&mut ecx).unwrap();

    // Process the result.
    match res {
        Ok(return_code) => {
            if !ignore_leaks {
                let leaks = ecx.memory.leak_report();
                if leaks != 0 {
                    tcx.sess.err("the evaluated program leaked memory");
                    // Ignore the provided return code - let the reported error
                    // determine the return code.
                    return None;
                }
            }
            Some(return_code)
        }
        Err(e) => report_error(&ecx, e),
    }
}
<commit_msg>Make an assert message consistent with other asserts.<commit_after>//! Main evaluator loop and setting up the initial stack frame.

use std::convert::TryFrom;
use std::ffi::OsStr;

use rand::rngs::StdRng;
use rand::SeedableRng;

use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, layout::LayoutCx, TyCtxt};
use rustc_target::abi::LayoutOf;

use crate::*;

/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
    /// Determine if validity checking is enabled.
    pub validate: bool,
    /// Determines if Stacked Borrows is enabled.
    pub stacked_borrows: bool,
    /// Determines if alignment checking is enabled.
    pub check_alignment: bool,
    /// Determines if communication with the host environment is enabled.
    pub communicate: bool,
    /// Determines if memory leaks should be ignored.
    pub ignore_leaks: bool,
    /// Environment variables that should always be isolated from the host.
    pub excluded_env_vars: Vec<String>,
    /// Command-line arguments passed to the interpreted program.
    pub args: Vec<String>,
    /// The seed to use when non-determinism or randomness are required (e.g. ptr-to-int cast, `getrandom()`).
    pub seed: Option<u64>,
    /// The stacked borrow id to report about
    pub tracked_pointer_tag: Option<PtrId>,
    /// The allocation id to report about.
    pub tracked_alloc_id: Option<AllocId>,
}

impl Default for MiriConfig {
    fn default() -> MiriConfig {
        MiriConfig {
            validate: true,
            stacked_borrows: true,
            check_alignment: true,
            communicate: false,
            ignore_leaks: false,
            excluded_env_vars: vec![],
            args: vec![],
            seed: None,
            tracked_pointer_tag: None,
            tracked_alloc_id: None,
        }
    }
}

/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
/// the location where the return value of the `start` lang item will be
/// written to.
/// Public because this is also used by `priroda`.
pub fn create_ecx<'mir, 'tcx: 'mir>(
    tcx: TyCtxt<'tcx>,
    main_id: DefId,
    config: MiriConfig,
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Tag>)> {
    let tcx_at = tcx.at(rustc_span::source_map::DUMMY_SP);
    let param_env = ty::ParamEnv::reveal_all();
    let layout_cx = LayoutCx { tcx, param_env };
    let mut ecx = InterpCx::new(
        tcx_at,
        param_env,
        Evaluator::new(config.communicate, config.validate, layout_cx),
        MemoryExtra::new(
            StdRng::seed_from_u64(config.seed.unwrap_or(0)),
            config.stacked_borrows,
            config.tracked_pointer_tag,
            config.tracked_alloc_id,
            config.check_alignment,
        ),
    );
    // Complete initialization.
    EnvVars::init(&mut ecx, config.excluded_env_vars)?;
    MemoryExtra::init_extern_statics(&mut ecx)?;

    // Setup first stack-frame
    let main_instance = ty::Instance::mono(tcx, main_id);
    let main_mir = ecx.load_mir(main_instance.def, None)?;
    if main_mir.arg_count != 0 {
        bug!("main function must not take any arguments");
    }

    let start_id = tcx.lang_items().start_fn().unwrap();
    let main_ret_ty = tcx.fn_sig(main_id).output();
    let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
    let start_instance = ty::Instance::resolve(
        tcx,
        ty::ParamEnv::reveal_all(),
        start_id,
        tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(main_ret_ty))),
    )
    .unwrap()
    .unwrap();

    // First argument: pointer to `main()`.
    let main_ptr = ecx.memory.create_fn_alloc(FnVal::Instance(main_instance));
    // Second argument (argc): length of `config.args`.
    let argc = Scalar::from_machine_usize(u64::try_from(config.args.len()).unwrap(), &ecx);
    // Third argument (`argv`): created from `config.args`.
    let argv = {
        // Put each argument in memory, collect pointers.
        let mut argvs = Vec::<Scalar<Tag>>::new();
        for arg in config.args.iter() {
            // Make space for `0` terminator.
            let size = u64::try_from(arg.len()).unwrap().checked_add(1).unwrap();
            let arg_type = tcx.mk_array(tcx.types.u8, size);
            let arg_place = ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Machine.into());
            ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?;
            argvs.push(arg_place.ptr);
        }
        // Make an array with all these pointers, in the Miri memory.
        let argvs_layout =
            ecx.layout_of(tcx.mk_array(tcx.mk_imm_ptr(tcx.types.u8), u64::try_from(argvs.len()).unwrap()))?;
        let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into());
        for (idx, arg) in argvs.into_iter().enumerate() {
            let place = ecx.mplace_field(argvs_place, idx)?;
            ecx.write_scalar(arg, place.into())?;
        }
        ecx.memory.mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
        // A pointer to that place is the 3rd argument for main.
        let argv = argvs_place.ptr;
        // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
        {
            let argc_place =
                ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into());
            ecx.write_scalar(argc, argc_place.into())?;
            ecx.machine.argc = Some(argc_place.ptr);

            let argv_place = ecx.allocate(
                ecx.layout_of(tcx.mk_imm_ptr(tcx.types.unit))?,
                MiriMemoryKind::Machine.into(),
            );
            ecx.write_scalar(argv, argv_place.into())?;
            ecx.machine.argv = Some(argv_place.ptr);
        }
        // Store command line as UTF-16 for Windows `GetCommandLineW`.
        {
            // Construct a command string with all the aguments.
            let mut cmd = String::new();
            for arg in config.args.iter() {
                if !cmd.is_empty() {
                    cmd.push(' ');
                }
                cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
            }
            // Don't forget `0` terminator.
            cmd.push(std::char::from_u32(0).unwrap());

            let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
            let cmd_type = tcx.mk_array(tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap());
            let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into());
            ecx.machine.cmd_line = Some(cmd_place.ptr);
            // Store the UTF-16 string. We just allocated so we know the bounds are fine.
            for (idx, &c) in cmd_utf16.iter().enumerate() {
                let place = ecx.mplace_field(cmd_place, idx)?;
                ecx.write_scalar(Scalar::from_u16(c), place.into())?;
            }
        }
        argv
    };

    // Return place (in static memory so that it does not count as leak).
    let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into());
    // Call start function.
    ecx.call_function(
        start_instance,
        &[main_ptr.into(), argc.into(), argv.into()],
        Some(ret_place.into()),
        StackPopCleanup::None { cleanup: true },
    )?;

    // Set the last_error to 0
    let errno_layout = ecx.machine.layouts.u32;
    let errno_place = ecx.allocate(errno_layout, MiriMemoryKind::Machine.into());
    ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?;
    ecx.machine.last_error = Some(errno_place);

    Ok((ecx, ret_place))
}

/// Evaluates the main function specified by `main_id`.
/// Returns `Some(return_code)` if program executed completed.
/// Returns `None` if an evaluation error occured.
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
    // FIXME: on Windows, we ignore leaks (https://github.com/rust-lang/miri/issues/1302).
    let ignore_leaks = config.ignore_leaks || tcx.sess.target.target.target_os == "windows";

    let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
        Ok(v) => v,
        Err(mut err) => {
            err.print_backtrace();
            panic!("Miri initialization error: {}", err.kind)
        }
    };

    // Perform the main execution.
    let res: InterpResult<'_, i64> = (|| {
        // Main loop.
        while ecx.schedule()? {
            assert!(ecx.step()?, "a terminated thread was scheduled for execution");
            ecx.process_diagnostics();
        }
        // Read the return code pointer *before* we run TLS destructors, to assert
        // that it was written to by the time that `start` lang item returned.
        let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
        // Global destructors.
        ecx.run_tls_dtors()?;
        Ok(return_code)
    })();

    // Machine cleanup.
    EnvVars::cleanup(&mut ecx).unwrap();

    // Process the result.
    match res {
        Ok(return_code) => {
            if !ignore_leaks {
                let leaks = ecx.memory.leak_report();
                if leaks != 0 {
                    tcx.sess.err("the evaluated program leaked memory");
                    // Ignore the provided return code - let the reported error
                    // determine the return code.
                    return None;
                }
            }
            Some(return_code)
        }
        Err(e) => report_error(&ecx, e),
    }
}
<|endoftext|>


<commit_before>use parser::parser::{parse};
use ast::Expr::*;
use ast::UnOp::*;
use ast::BinOp::*;
use ast::Dec::*;
use ast::*;
use state::*;
use runtime_error::RuntimeError;
use std::result;

pub type Result<T> = result::Result<T, RuntimeError>;

pub struct Repl {
  pub state: State,
}

impl Repl {
  pub fn new() -> Repl {
    Repl {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e.clone() {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Int(_) | Bool(_) | Func(_, _, _) | Undefined => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, ref e1) if e1.is_value() => {
        Bool(!e1.to_bool()?)
      },
      Uop(Neg, ref e1) if e1.is_value() => {
        Int(-1 * e1.to_int()?)
      },
      Bop(And, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_bool()? && e2.to_bool()?)
      },
      Bop(Or, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_bool()? || e2.to_bool()?)
      },
      Bop(Eq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 == *e2)
      },
      Bop(Ne, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 != *e2)
      },
      Bop(Mod, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        let n1 = e1.to_int()?;
        let n2 = e2.to_int()?;

        // rust % gives the remainder, not modulus
        let result = ((n1 % n2) + n2) % n2;

        Int(result)
      },
      Bop(Lt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? < e2.to_int()?)
      },
      Bop(Gt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? > e2.to_int()?)
      },
      Bop(Leq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? <= e2.to_int()?)
      },
      Bop(Geq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? >= e2.to_int()?)
      },
      Bop(Plus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? + e2.to_int()?)
      },
      Bop(Minus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? - e2.to_int()?)
      },
      Bop(Times, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? * e2.to_int()?)
      },
      Bop(Div, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? / e2.to_int()?)
      },
      Bop(Seq, ref v1, ref e2) if v1.is_value() => {
        *e2.clone()
      },
      Bop(Assign, ref v1, ref v2) if v1.is_var() && v2.is_value() => {
        let x = v1.to_var()?;
        self.state.assign(x, *v2.clone())?;
        debug!("done assigning {:?}", self.state.mem);
        *v2.clone()
      },
      Ternary(ref v1, ref e2, ref e3) if v1.is_value() => {
        match v1.to_bool()? {
          true => *e2.clone(),
          false => *e3.clone(),
        }
      },
      Decl(DConst, ref x, ref v1, ref e2) if v1.is_value() => {
        self.state.alloc_const(x.to_var()?, *v1.clone())?;
        *e2.clone()
      },
      Decl(DVar, ref x, ref v1, ref e2) if x.is_var() && v1.is_value() => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x.to_var()?, *v1.clone())?;
        *e2.clone()
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Func(ref name, ref e1, ref xs) => {
            self.state.begin_scope();

            // alloc the params
            for (xn, en) in xs.iter().zip(es.iter()) {
              self.state.alloc(xn.to_var()?, en.clone())?;
            }

            // alloc the fn body for named functions
            match *name {
              Some(ref s) => self.state.alloc(s.to_var()?, *v1.clone())?,
              _ => {},
            };

            Scope(Box::new(*e1.clone()))
          },
          _ => return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(ref v1) if v1.is_value() => {
        self.state.end_scope();
        *v1.clone()
      },
      Print(ref v1) if v1.is_value() => {
        println!("{}", v1);
        Expr::Undefined
      },
      /**
       * Search Cases
       */
      Bop(ref op, ref v1, ref e2) if v1.is_value() => {
        Bop(
          op.clone(),
          Box::new(*v1.clone()),
          Box::new(self.step(*e2.clone())?)
        )
      },
      Bop(Assign, ref v1, ref e2) if v1.is_var() => {
        Bop(
          Assign,
          Box::new(*v1.clone()),
          Box::new(self.step(*e2.clone())?)
        )
      },
      Bop(op, e1, e2) => {
        Bop(op, Box::new(self.step(*e1)?), e2)
      },
      Uop(op, e1) => {
        Uop(op, Box::new(self.step(*e1)?))
      },
      Ternary(e1, e2, e3) => {
        Ternary(Box::new(self.step(*e1)?), e2, e3)
      },
      While(ref v1, ref e1o, _, ref e2o, ref e3) if v1.is_value() => {
        match v1.to_bool()? {
          true => While(Box::new(*e1o.clone()), e1o.clone(), e2o.clone(), e2o.clone(), e3.clone()),
          false => *e3.clone(),
        }
      },
      While(ref e1, ref e1o, ref v2, ref e2o, ref e3) if v2.is_value() => {
        While(Box::new(self.step(*e1.clone())?), e1o.clone(), v2.clone(), e2o.clone(), e3.clone())
      },
      While(e1, e1o, e2, e2o, e3) => {
        While(e1, e1o, Box::new(self.step(*e2)?), e2o, e3)
      },
      Decl(dt, addr, e1, e2) => {
        Decl(dt, Box::new(*addr.clone()), Box::new(self.step(*e1)?), e2)
      },
      FnCall(ref v1, ref args) if v1.is_func() => {
        let mut found_nonvalue = false;

        let stepped_args: Result<Vec<Expr>> = args.iter().map(|e| {
          if !found_nonvalue && !e.is_value() {
            found_nonvalue = true;
            self.step(e.clone())
          } else {
            Ok(e.clone())
          }
        }).collect();

        FnCall(v1.clone(), stepped_args?)
      },
      FnCall(e1, args) => {
        FnCall(Box::new(self.step(*e1)?), args)
      },
      Scope(e1) => {
        Scope(Box::new(self.step(*e1)?))
      },
      Print(e1) => {
        Print(Box::new(self.step(*e1)?))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = parse(input)?;

    let mut num_iterations = 0;

    loop {
      if num_iterations > 100000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = self.step(e.clone())?;
      }
    }
  }
}

pub fn boxx(input: &str) -> Result<Expr> {
  let mut repl = Repl::new();
  repl.eval(input)
}
<commit_msg>move up do rule for while and increase max iterations<commit_after>use parser::parser::{parse};
use ast::Expr::*;
use ast::UnOp::*;
use ast::BinOp::*;
use ast::Dec::*;
use ast::*;
use state::*;
use runtime_error::RuntimeError;
use std::result;

pub type Result<T> = result::Result<T, RuntimeError>;

pub struct Repl {
  pub state: State,
}

impl Repl {
  pub fn new() -> Repl {
    Repl {
      state: State::new(),
    }
  }

  pub fn step(&mut self, e: Expr) -> Result<Expr> {
    debug!("step(e) : {:?}", e);
    debug!("step(self.state) : {:?}", self.state.mem);

    let e1 = match e.clone() {
      Var(x) => {
        match self.state.get(x.clone()) {
          Some(e) => e,
          None => return Err(RuntimeError::VariableNotFound(x)),
        }
      },
      /**
       * Values are ineligible for step
       */
      Int(_) | Bool(_) | Func(_, _, _) | Undefined => {
        debug!("stepping on a value {:?}", e);
        return Err(RuntimeError::SteppingOnValue(e));
      },
      /**
       * Base cases
       */
      Uop(Not, ref e1) if e1.is_value() => {
        Bool(!e1.to_bool()?)
      },
      Uop(Neg, ref e1) if e1.is_value() => {
        Int(-1 * e1.to_int()?)
      },
      Bop(And, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_bool()? && e2.to_bool()?)
      },
      Bop(Or, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_bool()? || e2.to_bool()?)
      },
      Bop(Eq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 == *e2)
      },
      Bop(Ne, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(*e1 != *e2)
      },
      Bop(Mod, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        let n1 = e1.to_int()?;
        let n2 = e2.to_int()?;

        // rust % gives the remainder, not modulus
        let result = ((n1 % n2) + n2) % n2;

        Int(result)
      },
      Bop(Lt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? < e2.to_int()?)
      },
      Bop(Gt, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? > e2.to_int()?)
      },
      Bop(Leq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? <= e2.to_int()?)
      },
      Bop(Geq, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Bool(e1.to_int()? >= e2.to_int()?)
      },
      Bop(Plus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? + e2.to_int()?)
      },
      Bop(Minus, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? - e2.to_int()?)
      },
      Bop(Times, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? * e2.to_int()?)
      },
      Bop(Div, ref e1, ref e2) if e1.is_value() && e2.is_value() => {
        Int(e1.to_int()? / e2.to_int()?)
      },
      Bop(Seq, ref v1, ref e2) if v1.is_value() => {
        *e2.clone()
      },
      Bop(Assign, ref v1, ref v2) if v1.is_var() && v2.is_value() => {
        let x = v1.to_var()?;
        self.state.assign(x, *v2.clone())?;
        debug!("done assigning {:?}", self.state.mem);
        *v2.clone()
      },
      Ternary(ref v1, ref e2, ref e3) if v1.is_value() => {
        match v1.to_bool()? {
          true => *e2.clone(),
          false => *e3.clone(),
        }
      },
      Decl(DConst, ref x, ref v1, ref e2) if v1.is_value() => {
        self.state.alloc_const(x.to_var()?, *v1.clone())?;
        *e2.clone()
      },
      Decl(DVar, ref x, ref v1, ref e2) if x.is_var() && v1.is_value() => {
        debug!("allocing {:?}", v1);
        self.state.alloc(x.to_var()?, *v1.clone())?;
        *e2.clone()
      },
      // lambda lift so we can use iter() in guard
      // https://github.com/rust-lang/rfcs/issues/1006
      FnCall(ref v1, ref es) if v1.is_func() && (|| es.iter().all(|v| v.is_value()))() => {
        match **v1 {
          Func(ref name, ref e1, ref xs) => {
            self.state.begin_scope();

            // alloc the params
            for (xn, en) in xs.iter().zip(es.iter()) {
              self.state.alloc(xn.to_var()?, en.clone())?;
            }

            // alloc the fn body for named functions
            match *name {
              Some(ref s) => self.state.alloc(s.to_var()?, *v1.clone())?,
              _ => {},
            };

            Scope(Box::new(*e1.clone()))
          },
          _ => return Err(RuntimeError::UnexpectedExpr("expected Func".to_string(), *v1.clone()))
        }
      },
      Scope(ref v1) if v1.is_value() => {
        self.state.end_scope();
        *v1.clone()
      },
      While(ref v1, ref e1o, _, ref e2o, ref e3) if v1.is_value() => {
        match v1.to_bool()? {
          true => While(Box::new(*e1o.clone()), e1o.clone(), e2o.clone(), e2o.clone(), e3.clone()),
          false => *e3.clone(),
        }
      },
      Print(ref v1) if v1.is_value() => {
        println!("{}", v1);
        Expr::Undefined
      },
      /**
       * Search Cases
       */
      Bop(ref op, ref v1, ref e2) if v1.is_value() => {
        Bop(
          op.clone(),
          Box::new(*v1.clone()),
          Box::new(self.step(*e2.clone())?)
        )
      },
      Bop(Assign, ref v1, ref e2) if v1.is_var() => {
        Bop(
          Assign,
          Box::new(*v1.clone()),
          Box::new(self.step(*e2.clone())?)
        )
      },
      Bop(op, e1, e2) => {
        Bop(op, Box::new(self.step(*e1)?), e2)
      },
      Uop(op, e1) => {
        Uop(op, Box::new(self.step(*e1)?))
      },
      Ternary(e1, e2, e3) => {
        Ternary(Box::new(self.step(*e1)?), e2, e3)
      },
      While(ref e1, ref e1o, ref v2, ref e2o, ref e3) if v2.is_value() => {
        While(Box::new(self.step(*e1.clone())?), e1o.clone(), v2.clone(), e2o.clone(), e3.clone())
      },
      While(e1, e1o, e2, e2o, e3) => {
        While(e1, e1o, Box::new(self.step(*e2)?), e2o, e3)
      },
      Decl(dt, addr, e1, e2) => {
        Decl(dt, Box::new(*addr.clone()), Box::new(self.step(*e1)?), e2)
      },
      FnCall(ref v1, ref args) if v1.is_func() => {
        let mut found_nonvalue = false;

        let stepped_args: Result<Vec<Expr>> = args.iter().map(|e| {
          if !found_nonvalue && !e.is_value() {
            found_nonvalue = true;
            self.step(e.clone())
          } else {
            Ok(e.clone())
          }
        }).collect();

        FnCall(v1.clone(), stepped_args?)
      },
      FnCall(e1, args) => {
        FnCall(Box::new(self.step(*e1)?), args)
      },
      Scope(e1) => {
        Scope(Box::new(self.step(*e1)?))
      },
      Print(e1) => {
        Print(Box::new(self.step(*e1)?))
      },
    };

    debug!("returning with mem {:?}" , self.state.mem);
    debug!("returning with e {:?}" , e1);
    Ok(e1)
  }

  pub fn eval(&mut self, input: &str) -> Result<Expr> {
    let mut e = parse(input)?;

    let mut num_iterations = 0;

    loop {
      if num_iterations > 1000000000 {
        return Err(RuntimeError::TooManyIterations(num_iterations))
      }

      debug!("-----------------");
      debug!("--- iterating on e {:?} ", e);
      debug!("--- iterating on m {:?} ", self.state.mem);
      num_iterations += 1;
      if e.is_value() {
        debug!("--- iterations: {}", num_iterations);
        return Ok(e.clone());
      } else {
        e = self.step(e.clone())?;
      }
    }
  }
}

pub fn boxx(input: &str) -> Result<Expr> {
  let mut repl = Repl::new();
  repl.eval(input)
}
<|endoftext|>


<commit_before>//! Types for the [`m.room.join_rules`] event.
//!
//! [`m.room.join_rules`]: https://spec.matrix.org/v1.2/client-server-api/#mroomjoin_rules

use std::{borrow::Cow, collections::BTreeMap};

use ruma_macros::EventContent;
use serde::{
    de::{Deserializer, Error},
    Deserialize, Serialize,
};
use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue};

use crate::{serde::from_raw_json_value, PrivOwnedStr, RoomId};

/// The content of an `m.room.join_rules` event.
///
/// Describes how users are allowed to join the room.
#[derive(Clone, Debug, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.join_rules", kind = State)]
pub struct RoomJoinRulesEventContent {
    /// The type of rules used for users wishing to join this room.
    #[ruma_event(skip_redaction)]
    #[serde(flatten)]
    pub join_rule: JoinRule,
}

impl RoomJoinRulesEventContent {
    /// Creates a new `RoomJoinRulesEventContent` with the given rule.
    pub fn new(join_rule: JoinRule) -> Self {
        Self { join_rule }
    }

    /// Creates a new `RoomJoinRulesEventContent` with the restricted rule and the given set of
    /// allow rules.
    pub fn restricted(allow: Vec<AllowRule>) -> Self {
        Self { join_rule: JoinRule::Restricted(Restricted::new(allow)) }
    }
}

impl<'de> Deserialize<'de> for RoomJoinRulesEventContent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let join_rule = JoinRule::deserialize(deserializer)?;
        Ok(RoomJoinRulesEventContent { join_rule })
    }
}

/// The rule used for users wishing to join this room.
///
/// This type can hold an arbitrary string. To check for formats that are not available as a
/// documented variant here, use its string representation, obtained through `.as_str()`.
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "join_rule")]
pub enum JoinRule {
    /// A user who wishes to join the room must first receive an invite to the room from someone
    /// already inside of the room.
    #[serde(rename = "invite")]
    Invite,

    /// Users can join the room if they are invited, or they can request an invite to the room.
    ///
    /// They can be allowed (invited) or denied (kicked/banned) access.
    #[serde(rename = "knock")]
    Knock,

    /// Reserved but not yet implemented by the Matrix specification.
    #[serde(rename = "private")]
    Private,

    /// Users can join the room if they are invited, or if they meet any of the conditions
    /// described in a set of [`AllowRule`]s.
    #[serde(rename = "restricted")]
    Restricted(Restricted),

    /// Anyone can join the room without any prior action.
    #[serde(rename = "public")]
    Public,

    #[doc(hidden)]
    #[serde(skip_serializing)]
    _Custom(PrivOwnedStr),
}

impl JoinRule {
    /// Returns the string name of this `JoinRule`
    pub fn as_str(&self) -> &str {
        match self {
            JoinRule::Invite => "invite",
            JoinRule::Knock => "knock",
            JoinRule::Private => "private",
            JoinRule::Restricted(_) => "restricted",
            JoinRule::Public => "public",
            JoinRule::_Custom(rule) => &rule.0,
        }
    }
}

impl<'de> Deserialize<'de> for JoinRule {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let json: Box<RawJsonValue> = Box::deserialize(deserializer)?;

        #[derive(Deserialize)]
        struct ExtractType<'a> {
            join_rule: Option<Cow<'a, str>>,
        }

        let join_rule = serde_json::from_str::<ExtractType<'_>>(json.get())
            .map_err(serde::de::Error::custom)?
            .join_rule
            .ok_or_else(|| D::Error::missing_field("join_rule"))?;

        match join_rule.as_ref() {
            "invite" => Ok(Self::Invite),
            "knock" => Ok(Self::Knock),
            "private" => Ok(Self::Private),
            "restricted" => from_raw_json_value(&json).map(Self::Restricted),
            "public" => Ok(Self::Public),
            _ => Ok(Self::_Custom(PrivOwnedStr(join_rule.into()))),
        }
    }
}

/// Configuration of the `Restricted` join rule.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Restricted {
    /// Allow rules which describe conditions that allow joining a room.
    allow: Vec<AllowRule>,
}

impl Restricted {
    /// Constructs a new rule set for restricted rooms with the given rules.
    pub fn new(allow: Vec<AllowRule>) -> Self {
        Self { allow }
    }
}

/// An allow rule which defines a condition that allows joining a room.
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type")]
pub enum AllowRule {
    /// Joining is allowed if a user is already a member of the room with the id `room_id`.
    #[serde(rename = "m.room_membership")]
    RoomMembership(RoomMembership),

    #[doc(hidden)]
    _Custom(CustomAllowRule),
}

impl AllowRule {
    /// Constructs an `AllowRule` with membership of the room with the given id as its predicate.
    pub fn room_membership(room_id: Box<RoomId>) -> Self {
        Self::RoomMembership(RoomMembership::new(room_id))
    }
}

/// Allow rule which grants permission to join based on the membership of another room.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RoomMembership {
    /// The id of the room which being a member of grants permission to join another room.
    pub room_id: Box<RoomId>,
}

impl RoomMembership {
    /// Constructs a new room membership rule for the given room id.
    pub fn new(room_id: Box<RoomId>) -> Self {
        Self { room_id }
    }
}

#[doc(hidden)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct CustomAllowRule {
    #[serde(rename = "type")]
    rule_type: String,
    #[serde(flatten)]
    extra: BTreeMap<String, JsonValue>,
}

impl<'de> Deserialize<'de> for AllowRule {
    fn deserialize<D>(deserializer: D) -> Result<AllowRule, D::Error>
    where
        D: Deserializer<'de>,
    {
        let json: Box<RawJsonValue> = Box::deserialize(deserializer)?;

        // Extracts the `type` value.
        #[derive(Deserialize)]
        struct ExtractType<'a> {
            #[serde(borrow, rename = "type")]
            rule_type: Option<Cow<'a, str>>,
        }

        // Get the value of `type` if present.
        let rule_type = serde_json::from_str::<ExtractType<'_>>(json.get())
            .map_err(serde::de::Error::custom)?
            .rule_type;

        match rule_type.as_deref() {
            Some("m.room_membership") => from_raw_json_value(&json).map(Self::RoomMembership),
            Some(_) => from_raw_json_value(&json).map(Self::_Custom),
            None => Err(D::Error::missing_field("type")),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::room_id;
    use matches::assert_matches;

    use super::{AllowRule, JoinRule, RoomJoinRulesEventContent, SyncRoomJoinRulesEvent};

    #[test]
    fn deserialize() {
        let json = r#"{"join_rule": "public"}"#;
        let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
        assert_matches!(event, RoomJoinRulesEventContent { join_rule: JoinRule::Public });
    }

    #[test]
    fn deserialize_restricted() {
        let json = r#"{
            "join_rule": "restricted",
            "allow": [
                {
                    "type": "m.room_membership",
                    "room_id": "!mods:example.org"
                },
                {
                    "type": "m.room_membership",
                    "room_id": "!users:example.org"
                }
            ]
        }"#;
        let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
        match event.join_rule {
            JoinRule::Restricted(restricted) => assert_eq!(
                restricted.allow,
                &[
                    AllowRule::room_membership(room_id!("!mods:example.org").to_owned()),
                    AllowRule::room_membership(room_id!("!users:example.org").to_owned())
                ]
            ),
            rule => panic!("Deserialized to wrong variant: {:?}", rule),
        }
    }

    #[test]
    fn deserialize_restricted_event() {
        let json = r#"{
            "type": "m.room.join_rules",
            "sender": "@admin:community.rs",
            "content": {
                "join_rule": "restricted",
                "allow":[
                    { "type": "m.room_membership","room_id": "!KqeUnzmXPIhHRaWMTs:mccarty.io" }
                ]
            },
            "state_key": "",
            "origin_server_ts":1630508835342,
            "unsigned": {
                "age":4165521871
            },
            "event_id": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
        }"#;

        assert_matches!(serde_json::from_str::<SyncRoomJoinRulesEvent>(json), Ok(_));
    }
}
<commit_msg>common: Use serde(borrow) to avoid unnecessary copying<commit_after>//! Types for the [`m.room.join_rules`] event.
//!
//! [`m.room.join_rules`]: https://spec.matrix.org/v1.2/client-server-api/#mroomjoin_rules

use std::{borrow::Cow, collections::BTreeMap};

use ruma_macros::EventContent;
use serde::{
    de::{Deserializer, Error},
    Deserialize, Serialize,
};
use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue};

use crate::{serde::from_raw_json_value, PrivOwnedStr, RoomId};

/// The content of an `m.room.join_rules` event.
///
/// Describes how users are allowed to join the room.
#[derive(Clone, Debug, Serialize, EventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.join_rules", kind = State)]
pub struct RoomJoinRulesEventContent {
    /// The type of rules used for users wishing to join this room.
    #[ruma_event(skip_redaction)]
    #[serde(flatten)]
    pub join_rule: JoinRule,
}

impl RoomJoinRulesEventContent {
    /// Creates a new `RoomJoinRulesEventContent` with the given rule.
    pub fn new(join_rule: JoinRule) -> Self {
        Self { join_rule }
    }

    /// Creates a new `RoomJoinRulesEventContent` with the restricted rule and the given set of
    /// allow rules.
    pub fn restricted(allow: Vec<AllowRule>) -> Self {
        Self { join_rule: JoinRule::Restricted(Restricted::new(allow)) }
    }
}

impl<'de> Deserialize<'de> for RoomJoinRulesEventContent {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let join_rule = JoinRule::deserialize(deserializer)?;
        Ok(RoomJoinRulesEventContent { join_rule })
    }
}

/// The rule used for users wishing to join this room.
///
/// This type can hold an arbitrary string. To check for formats that are not available as a
/// documented variant here, use its string representation, obtained through `.as_str()`.
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "join_rule")]
pub enum JoinRule {
    /// A user who wishes to join the room must first receive an invite to the room from someone
    /// already inside of the room.
    #[serde(rename = "invite")]
    Invite,

    /// Users can join the room if they are invited, or they can request an invite to the room.
    ///
    /// They can be allowed (invited) or denied (kicked/banned) access.
    #[serde(rename = "knock")]
    Knock,

    /// Reserved but not yet implemented by the Matrix specification.
    #[serde(rename = "private")]
    Private,

    /// Users can join the room if they are invited, or if they meet any of the conditions
    /// described in a set of [`AllowRule`]s.
    #[serde(rename = "restricted")]
    Restricted(Restricted),

    /// Anyone can join the room without any prior action.
    #[serde(rename = "public")]
    Public,

    #[doc(hidden)]
    #[serde(skip_serializing)]
    _Custom(PrivOwnedStr),
}

impl JoinRule {
    /// Returns the string name of this `JoinRule`
    pub fn as_str(&self) -> &str {
        match self {
            JoinRule::Invite => "invite",
            JoinRule::Knock => "knock",
            JoinRule::Private => "private",
            JoinRule::Restricted(_) => "restricted",
            JoinRule::Public => "public",
            JoinRule::_Custom(rule) => &rule.0,
        }
    }
}

impl<'de> Deserialize<'de> for JoinRule {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let json: Box<RawJsonValue> = Box::deserialize(deserializer)?;

        #[derive(Deserialize)]
        struct ExtractType<'a> {
            #[serde(borrow)]
            join_rule: Option<Cow<'a, str>>,
        }

        let join_rule = serde_json::from_str::<ExtractType<'_>>(json.get())
            .map_err(serde::de::Error::custom)?
            .join_rule
            .ok_or_else(|| D::Error::missing_field("join_rule"))?;

        match join_rule.as_ref() {
            "invite" => Ok(Self::Invite),
            "knock" => Ok(Self::Knock),
            "private" => Ok(Self::Private),
            "restricted" => from_raw_json_value(&json).map(Self::Restricted),
            "public" => Ok(Self::Public),
            _ => Ok(Self::_Custom(PrivOwnedStr(join_rule.into()))),
        }
    }
}

/// Configuration of the `Restricted` join rule.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct Restricted {
    /// Allow rules which describe conditions that allow joining a room.
    allow: Vec<AllowRule>,
}

impl Restricted {
    /// Constructs a new rule set for restricted rooms with the given rules.
    pub fn new(allow: Vec<AllowRule>) -> Self {
        Self { allow }
    }
}

/// An allow rule which defines a condition that allows joining a room.
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "type")]
pub enum AllowRule {
    /// Joining is allowed if a user is already a member of the room with the id `room_id`.
    #[serde(rename = "m.room_membership")]
    RoomMembership(RoomMembership),

    #[doc(hidden)]
    _Custom(CustomAllowRule),
}

impl AllowRule {
    /// Constructs an `AllowRule` with membership of the room with the given id as its predicate.
    pub fn room_membership(room_id: Box<RoomId>) -> Self {
        Self::RoomMembership(RoomMembership::new(room_id))
    }
}

/// Allow rule which grants permission to join based on the membership of another room.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RoomMembership {
    /// The id of the room which being a member of grants permission to join another room.
    pub room_id: Box<RoomId>,
}

impl RoomMembership {
    /// Constructs a new room membership rule for the given room id.
    pub fn new(room_id: Box<RoomId>) -> Self {
        Self { room_id }
    }
}

#[doc(hidden)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct CustomAllowRule {
    #[serde(rename = "type")]
    rule_type: String,
    #[serde(flatten)]
    extra: BTreeMap<String, JsonValue>,
}

impl<'de> Deserialize<'de> for AllowRule {
    fn deserialize<D>(deserializer: D) -> Result<AllowRule, D::Error>
    where
        D: Deserializer<'de>,
    {
        let json: Box<RawJsonValue> = Box::deserialize(deserializer)?;

        // Extracts the `type` value.
        #[derive(Deserialize)]
        struct ExtractType<'a> {
            #[serde(borrow, rename = "type")]
            rule_type: Option<Cow<'a, str>>,
        }

        // Get the value of `type` if present.
        let rule_type = serde_json::from_str::<ExtractType<'_>>(json.get())
            .map_err(serde::de::Error::custom)?
            .rule_type;

        match rule_type.as_deref() {
            Some("m.room_membership") => from_raw_json_value(&json).map(Self::RoomMembership),
            Some(_) => from_raw_json_value(&json).map(Self::_Custom),
            None => Err(D::Error::missing_field("type")),
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::room_id;
    use matches::assert_matches;

    use super::{AllowRule, JoinRule, RoomJoinRulesEventContent, SyncRoomJoinRulesEvent};

    #[test]
    fn deserialize() {
        let json = r#"{"join_rule": "public"}"#;
        let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
        assert_matches!(event, RoomJoinRulesEventContent { join_rule: JoinRule::Public });
    }

    #[test]
    fn deserialize_restricted() {
        let json = r#"{
            "join_rule": "restricted",
            "allow": [
                {
                    "type": "m.room_membership",
                    "room_id": "!mods:example.org"
                },
                {
                    "type": "m.room_membership",
                    "room_id": "!users:example.org"
                }
            ]
        }"#;
        let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
        match event.join_rule {
            JoinRule::Restricted(restricted) => assert_eq!(
                restricted.allow,
                &[
                    AllowRule::room_membership(room_id!("!mods:example.org").to_owned()),
                    AllowRule::room_membership(room_id!("!users:example.org").to_owned())
                ]
            ),
            rule => panic!("Deserialized to wrong variant: {:?}", rule),
        }
    }

    #[test]
    fn deserialize_restricted_event() {
        let json = r#"{
            "type": "m.room.join_rules",
            "sender": "@admin:community.rs",
            "content": {
                "join_rule": "restricted",
                "allow":[
                    { "type": "m.room_membership","room_id": "!KqeUnzmXPIhHRaWMTs:mccarty.io" }
                ]
            },
            "state_key": "",
            "origin_server_ts":1630508835342,
            "unsigned": {
                "age":4165521871
            },
            "event_id": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
        }"#;

        assert_matches!(serde_json::from_str::<SyncRoomJoinRulesEvent>(json), Ok(_));
    }
}
<|endoftext|>


<commit_before>use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::cmp;
use std::fmt;

pub struct Cursor {
    pub x: i32,
    pub y: i32,
    y_offset: i32,
}

impl fmt::Display for Cursor {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "({}, {}, o={})", self.x, self.y, self.y_offset)
    }
}

impl Cursor {
    fn curr_len(&self, lines: &Vec<String>) -> i32 {
        lines[self.y as usize - 1].len() as i32
    }

    fn move_left(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if self.x > 1 {
            self.x -= 1;
        } else if self.y > 1 {
            self.move_up(dim, lines);
            self.x = self.curr_len(lines) + 1;
        }
    }

    fn move_right(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if self.x <= self.curr_len(lines) {
            self.x += 1;
        } else if self.y < lines.len() as i32 {
            self.move_home(dim, lines);
            self.move_down(dim, lines);
        }
    }

    fn move_up(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if self.x > dim.0 {
            self.x -= dim.0;
        } else if self.y > 1 {
            self.y -= 1;
            let line_len = self.curr_len(lines);
            let extra_lines = line_len / dim.0;
            self.y_offset -= extra_lines;
            self.x += extra_lines * dim.0;
            if self.x > line_len + 1 {
                self.x = line_len + 1;
            }
        }
    }

    fn move_down(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if (self.x - 1) / dim.0 < self.curr_len(lines) / dim.0 {
            self.x += dim.0;
        } else if self.y < lines.len() as i32 {
            self.y += 1;
            while self.x > dim.0 {
                self.x -= dim.0;
                self.y_offset += 1;
            }
        }
        if self.x > self.curr_len(lines) + 1 {
            self.x = self.curr_len(lines) + 1;
        }
    }

    fn move_home(&mut self, dim: (i32, i32), _: &Vec<String>) {
        // Depend on truncation here to clamp to lower multiple of screen width
        self.x = ((self.x - 1) / dim.0) * dim.0 + 1;
    }

    fn move_end(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        self.x = ((self.x - 1) / dim.0 + 1) * dim.0;
        if self.x > self.curr_len(lines) + 1 {
            self.x = self.curr_len(lines) + 1;
        }
    }

    fn project(&self, dim: (i32, i32)) -> Cursor {
        Cursor {
            x: (self.x - 1) % dim.0 + 1,
            y: self.y + (self.x - 1) / dim.0 + self.y_offset,
            y_offset: 0,
        }
    }

    fn recompute_offset(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        self.y_offset = 0;
        for i in 0..(self.y - 1) {
            self.y_offset += lines[i as usize].len() as i32 / dim.0;
        }
    }
}

pub struct File {
    pub name: String,
    pub lines: Vec<String>,
    caret: Cursor,
    window_top: Cursor,
    last_dim: (i32, i32),
    misc: String,
}

impl File {
    pub fn debug(&self, dim: (i32, i32)) -> String {
        format!("Caret {}, Top {}, Cursor {} {}",
            self.caret, self.window_top, self.cursor(dim), self.misc
        )
    }

    pub fn empty() -> File {
        File {
            name: String::from(""),
            lines: vec![String::from("")],
            caret: Cursor { x: 1, y: 1, y_offset: 0 },
            window_top: Cursor { x: 1, y: 1, y_offset: 0 },
            last_dim: (0, 0),
            misc: String::from(""),
        }
    }

    pub fn open(path: &str) -> File {
        let f = fs::File::open(path).expect("Could not open file");
        let f = io::BufReader::new(f);

        File {
            name: String::from(Path::new(path).file_name().unwrap().to_string_lossy()),
            lines: f.lines().map(|r| r.unwrap()).collect(),
            caret: Cursor { x: 1, y: 1, y_offset: 0 },
            window_top: Cursor { x: 1, y: 1, y_offset: 0 },
            last_dim: (0, 0),
            misc: String::from(""),
        }
    }

    pub fn save(&self, path: &str) {
        let f = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(path)
            .expect("Could not open file");
        let mut f = io::BufWriter::new(f);

        for line in self.lines.iter() {
            write!(f, "{}\n", line).unwrap();
        }
    }

    pub fn cursor(&self, dim: (i32, i32)) -> Cursor {
        let projected_caret = self.caret.project(dim);
        let projected_top = self.window_top.project(dim);
        Cursor {
            x: projected_caret.x,
            y: projected_caret.y - projected_top.y + 1,
            y_offset: 0,
        }
    }

    pub fn lineno_chars(&self) -> i32 {
        format!("{}", self.lines.len()).len() as i32
    }

    pub fn wrapped_lines(&self, dim: (i32, i32)) -> Vec<(Option<u16>, String)> {
        let mut result = vec![];
        let width = dim.0 as usize;
        let top_y = self.window_top.y as usize - 1;
        let top_extra = (self.window_top.x - 1) / dim.0;
        for (line_number, raw_line) in self.lines.iter().enumerate().skip(top_y) {
            let mut line_start = 0;
            let mut line_end = cmp::min(raw_line.len(), width);
            result.push((Some(line_number as u16),
                         String::from(&raw_line[line_start..line_end])));
            while line_end < raw_line.len() {
                line_start = line_end;
                line_end += cmp::min(raw_line.len() - line_end, width);
                result.push((None,
                             String::from(&raw_line[line_start..line_end])));
            }
            if result.len() as i32 >= dim.1 + top_extra {
                break;
            }
        }
        result.truncate((dim.1 + top_extra) as usize);
        result.drain(..top_extra as usize);
        result
    }

    fn current_line(&self) -> &String {
        &self.lines[self.caret.y as usize - 1]
    }

    fn recompute_offsets(&mut self, dim: (i32, i32)) {
        if dim != self.last_dim {
            self.caret.recompute_offset(dim, &self.lines);
            self.window_top.recompute_offset(dim, &self.lines);
            self.last_dim = dim;
        }
    }

    pub fn refresh(&mut self, dim: (i32, i32)) {
        self.recompute_offsets(dim);
    }

    pub fn move_cursor_left(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_left(dim, &self.lines);
        if self.cursor(dim).y < 1 {
            self.window_top.move_up(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_right(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_right(dim, &self.lines);
        if self.cursor(dim).y > dim.1 {
            self.window_top.move_down(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_up(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_up(dim, &self.lines);
        if self.cursor(dim).y < 1 {
            self.window_top.move_up(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_down(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_down(dim, &self.lines);
        if self.cursor(dim).y > dim.1 {
            self.window_top.move_down(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_home(&mut self, dim: (i32, i32)) {
        self.recompute_offsets(dim);
        self.caret.move_home(dim, &self.lines);
    }

    pub fn move_cursor_end(&mut self, dim: (i32, i32)) {
        self.recompute_offsets(dim);
        self.caret.move_end(dim, &self.lines);
    }

    pub fn page_up(&mut self, dim: (i32, i32)) {
        for _ in 0..dim.1 {
            self.move_cursor_up(dim);
        }
    }

    pub fn page_down(&mut self, dim: (i32, i32)) {
        for _ in 0..dim.1 {
            self.move_cursor_down(dim);
        }
    }

    pub fn insert(&mut self, dim: (i32, i32), c: char) {
        {
            let x = self.caret.x - 1;
            let line = &mut self.lines[self.caret.y as usize - 1];
            let pos = if x > line.len() as i32 {
                self.caret.x = line.len() as i32 - 1;
                line.len()
            } else {
                x as usize
            };
            line.insert(pos, c);
        }
        self.move_cursor_right(dim);
    }

    pub fn delete(&mut self, _: (i32, i32)) {
        let x = self.caret.x as usize - 1;
        if x == self.current_line().len() {
            let y = self.caret.y as usize - 1;
            if y < self.lines.len() - 1 {
                let next_line = self.lines.remove(y + 1);
                self.lines[y].push_str(&next_line);
            }
        } else {
            let line = &mut self.lines[self.caret.y as usize - 1];
            line.remove(x);
        }
    }

    pub fn backspace(&mut self, dim: (i32, i32)) {
        self.move_cursor_left(dim);
        self.delete(dim);
    }

    pub fn insert_newline(&mut self, dim: (i32, i32)) {
        let after = {
            let before = &mut self.lines[self.caret.y as usize - 1];
            before.split_off(self.caret.x as usize - 1)
        };
        self.lines.insert(self.caret.y as usize, after);
        self.move_cursor_right(dim);
    }
}
<commit_msg>Attempt to test file save weirdness<commit_after>use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::cmp;
use std::fmt;

pub struct Cursor {
    pub x: i32,
    pub y: i32,
    y_offset: i32,
}

impl fmt::Display for Cursor {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "({}, {}, o={})", self.x, self.y, self.y_offset)
    }
}

impl Cursor {
    fn curr_len(&self, lines: &Vec<String>) -> i32 {
        lines[self.y as usize - 1].len() as i32
    }

    fn move_left(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if self.x > 1 {
            self.x -= 1;
        } else if self.y > 1 {
            self.move_up(dim, lines);
            self.x = self.curr_len(lines) + 1;
        }
    }

    fn move_right(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if self.x <= self.curr_len(lines) {
            self.x += 1;
        } else if self.y < lines.len() as i32 {
            self.move_home(dim, lines);
            self.move_down(dim, lines);
        }
    }

    fn move_up(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if self.x > dim.0 {
            self.x -= dim.0;
        } else if self.y > 1 {
            self.y -= 1;
            let line_len = self.curr_len(lines);
            let extra_lines = line_len / dim.0;
            self.y_offset -= extra_lines;
            self.x += extra_lines * dim.0;
            if self.x > line_len + 1 {
                self.x = line_len + 1;
            }
        }
    }

    fn move_down(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        if (self.x - 1) / dim.0 < self.curr_len(lines) / dim.0 {
            self.x += dim.0;
        } else if self.y < lines.len() as i32 {
            self.y += 1;
            while self.x > dim.0 {
                self.x -= dim.0;
                self.y_offset += 1;
            }
        }
        if self.x > self.curr_len(lines) + 1 {
            self.x = self.curr_len(lines) + 1;
        }
    }

    fn move_home(&mut self, dim: (i32, i32), _: &Vec<String>) {
        // Depend on truncation here to clamp to lower multiple of screen width
        self.x = ((self.x - 1) / dim.0) * dim.0 + 1;
    }

    fn move_end(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        self.x = ((self.x - 1) / dim.0 + 1) * dim.0;
        if self.x > self.curr_len(lines) + 1 {
            self.x = self.curr_len(lines) + 1;
        }
    }

    fn project(&self, dim: (i32, i32)) -> Cursor {
        Cursor {
            x: (self.x - 1) % dim.0 + 1,
            y: self.y + (self.x - 1) / dim.0 + self.y_offset,
            y_offset: 0,
        }
    }

    fn recompute_offset(&mut self, dim: (i32, i32), lines: &Vec<String>) {
        self.y_offset = 0;
        for i in 0..(self.y - 1) {
            self.y_offset += lines[i as usize].len() as i32 / dim.0;
        }
    }
}

pub struct File {
    pub name: String,
    pub lines: Vec<String>,
    caret: Cursor,
    window_top: Cursor,
    last_dim: (i32, i32),
    misc: String,
}

impl File {
    pub fn debug(&self, dim: (i32, i32)) -> String {
        format!("Caret {}, Top {}, Cursor {} {}",
            self.caret, self.window_top, self.cursor(dim), self.misc
        )
    }

    pub fn empty() -> File {
        File {
            name: String::from(""),
            lines: vec![String::from("")],
            caret: Cursor { x: 1, y: 1, y_offset: 0 },
            window_top: Cursor { x: 1, y: 1, y_offset: 0 },
            last_dim: (0, 0),
            misc: String::from(""),
        }
    }

    pub fn open(path: &str) -> File {
        let f = fs::File::open(path).expect("Could not open file");
        let f = io::BufReader::new(f);

        File {
            name: String::from(Path::new(path).file_name().unwrap().to_string_lossy()),
            lines: f.lines().map(|r| r.unwrap()).collect(),
            caret: Cursor { x: 1, y: 1, y_offset: 0 },
            window_top: Cursor { x: 1, y: 1, y_offset: 0 },
            last_dim: (0, 0),
            misc: String::from(""),
        }
    }

    pub fn save(&self, path: &str) {
        let f = fs::OpenOptions::new()
            .write(true)
            .create(true)
            .open(path)
            .expect("Could not open file");
        let mut f = io::BufWriter::new(f);

        for line in self.lines.iter() {
            write!(f, "{}\n", line).unwrap();
        }
    }

    pub fn cursor(&self, dim: (i32, i32)) -> Cursor {
        let projected_caret = self.caret.project(dim);
        let projected_top = self.window_top.project(dim);
        Cursor {
            x: projected_caret.x,
            y: projected_caret.y - projected_top.y + 1,
            y_offset: 0,
        }
    }

    pub fn lineno_chars(&self) -> i32 {
        format!("{}", self.lines.len()).len() as i32
    }

    pub fn wrapped_lines(&self, dim: (i32, i32)) -> Vec<(Option<u16>, String)> {
        let mut result = vec![];
        let width = dim.0 as usize;
        let top_y = self.window_top.y as usize - 1;
        let top_extra = (self.window_top.x - 1) / dim.0;
        for (line_number, raw_line) in self.lines.iter().enumerate().skip(top_y) {
            let mut line_start = 0;
            let mut line_end = cmp::min(raw_line.len(), width);
            result.push((Some(line_number as u16),
                         String::from(&raw_line[line_start..line_end])));
            while line_end < raw_line.len() {
                line_start = line_end;
                line_end += cmp::min(raw_line.len() - line_end, width);
                result.push((None,
                             String::from(&raw_line[line_start..line_end])));
            }
            if result.len() as i32 >= dim.1 + top_extra {
                break;
            }
        }
        result.truncate((dim.1 + top_extra) as usize);
        result.drain(..top_extra as usize);
        result
    }

    fn current_line(&self) -> &String {
        &self.lines[self.caret.y as usize - 1]
    }

    fn recompute_offsets(&mut self, dim: (i32, i32)) {
        if dim != self.last_dim {
            self.caret.recompute_offset(dim, &self.lines);
            self.window_top.recompute_offset(dim, &self.lines);
            self.last_dim = dim;
        }
    }

    pub fn refresh(&mut self, dim: (i32, i32)) {
        self.recompute_offsets(dim);
    }

    pub fn move_cursor_left(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_left(dim, &self.lines);
        if self.cursor(dim).y < 1 {
            self.window_top.move_up(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_right(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_right(dim, &self.lines);
        if self.cursor(dim).y > dim.1 {
            self.window_top.move_down(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_up(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_up(dim, &self.lines);
        if self.cursor(dim).y < 1 {
            self.window_top.move_up(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_down(&mut self, dim: (i32, i32)) -> bool {
        self.recompute_offsets(dim);
        self.caret.move_down(dim, &self.lines);
        if self.cursor(dim).y > dim.1 {
            self.window_top.move_down(dim, &self.lines);
            true
        } else {
            false
        }
    }

    pub fn move_cursor_home(&mut self, dim: (i32, i32)) {
        self.recompute_offsets(dim);
        self.caret.move_home(dim, &self.lines);
    }

    pub fn move_cursor_end(&mut self, dim: (i32, i32)) {
        self.recompute_offsets(dim);
        self.caret.move_end(dim, &self.lines);
    }

    pub fn page_up(&mut self, dim: (i32, i32)) {
        for _ in 0..dim.1 {
            self.move_cursor_up(dim);
        }
    }

    pub fn page_down(&mut self, dim: (i32, i32)) {
        for _ in 0..dim.1 {
            self.move_cursor_down(dim);
        }
    }

    pub fn insert(&mut self, dim: (i32, i32), c: char) {
        {
            let x = self.caret.x - 1;
            let line = &mut self.lines[self.caret.y as usize - 1];
            let pos = if x > line.len() as i32 {
                self.caret.x = line.len() as i32 - 1;
                line.len()
            } else {
                x as usize
            };
            line.insert(pos, c);
        }
        self.move_cursor_right(dim);
    }

    pub fn delete(&mut self, _: (i32, i32)) {
        let x = self.caret.x as usize - 1;
        if x == self.current_line().len() {
            let y = self.caret.y as usize - 1;
            if y < self.lines.len() - 1 {
                let next_line = self.lines.remove(y + 1);
                self.lines[y].push_str(&next_line);
            }
        } else {
            let line = &mut self.lines[self.caret.y as usize - 1];
            line.remove(x);
        }
    }

    pub fn backspace(&mut self, dim: (i32, i32)) {
        self.move_cursor_left(dim);
        self.delete(dim);
    }

    pub fn insert_newline(&mut self, dim: (i32, i32)) {
        let after = {
            let before = &mut self.lines[self.caret.y as usize - 1];
            before.split_off(self.caret.x as usize - 1)
        };
        self.lines.insert(self.caret.y as usize, after);
        self.move_cursor_right(dim);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn load_save_preserves_everything() {
        let f = File::open("README.md");
        f.save("readme.bak");
        let orig = fs::File::open("README.md").unwrap();
        let new = fs::File::open("readme.bak").unwrap();
        for (b1, b2) in orig.bytes().zip(new.bytes()) {
            assert_eq!(b1.unwrap(), b2.unwrap());
        }
        fs::remove_file("readme.bak").unwrap();
    }
}
<|endoftext|>