<commit_before>//   Copyright 2014 Colin Sherratt
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

use geometry::{VertexBuffer, Geometry, VertexGeoTexNorm};
use material::Material;
use Graphics;

use genmesh::generators::{Plane, Cube, SphereUV};
use genmesh::{MapToVertices, Indexer, LruIndexer};
use genmesh::{Vertices, Triangulate, Quad, Triangle};
use genmesh::{Polygon, PolyTri, PolyQuad};
use cgmath::{Vector3, EuclideanVector};

use std::num::pow;

static WEB_COLORS: [(&'static str, [f32, ..3]), ..16] = [
    ("white",   [1., 1., 1.]),
    ("silver",  [0.75, 0.75, 0.75]),
    ("gray",    [0.5, 0.5, 0.5]),
    ("black",   [0., 0., 0.]),
    ("red",     [1., 0., 0.]),
    ("maroon",  [0.5, 0., 0.]),
    ("yellow",  [1., 1., 0.]),
    ("olive",   [0.5, 0.5, 0.]),
    ("line",    [0., 1., 0.]),
    ("green",   [0., 0.5, 0.]),
    ("aqua",    [0., 1., 1.]),
    ("teal",    [0., 0.5, 0.5]),
    ("blue",    [0., 0., 1.]),
    ("navy",    [0., 0., 0.5]),
    ("fuchsia", [1., 0., 1.]),
    ("pruple",  [0.5, 0., 0.5]),
];

fn build_vectors<T: Iterator<Quad<VertexGeoTexNorm>>>(input: T)
    -> (Vec<VertexGeoTexNorm>, Vec<u32>) {

    let mut mesh_data: Vec<VertexGeoTexNorm> = Vec::new();
    let mut index: Vec<u32> = {
        let mut indexer = LruIndexer::new(16, |_, v| mesh_data.push(v));
        input.map(|mut p| {
            let a = Vector3::new(p.x.position[0],
                                 p.x.position[1],
                                 p.x.position[2]);
            let b = Vector3::new(p.y.position[0],
                                 p.y.position[1],
                                 p.y.position[2]);
            let c = Vector3::new(p.z.position[0],
                                 p.z.position[1],
                                 p.z.position[2]);

            let normal = (a - b).cross(&(b - c)).normalize();

            p.x.normal = [normal.x, normal.y, normal.z];
            p.y.normal = [normal.x, normal.y, normal.z];
            p.z.normal = [normal.x, normal.y, normal.z];
            p.w.normal = [normal.x, normal.y, normal.z];

            p.x.texture = [-1., -1.];
            p.y.texture = [-1.,  1.];
            p.z.texture = [ 1.,  1.];
            p.w.texture = [ 1., -1.];

            p
        })
        .vertex(|v| indexer.index(v) as u32)
        .triangulate()
        .vertices()
        .collect()
    };

    (mesh_data, index)
}

fn build_vectors_poly<T: Iterator<Polygon<VertexGeoTexNorm>>>(input: T)
    -> (Vec<VertexGeoTexNorm>, Vec<u32>) {

    let mut mesh_data: Vec<VertexGeoTexNorm> = Vec::new();
    let mut index: Vec<u32> = {
        let mut indexer = LruIndexer::new(16, |_, v| mesh_data.push(v));
        input.map(|mut p| {
            let (a, b, c) = match p {
                PolyQuad(Quad{x: a, y: b, z: c, w: _}) => (a, b, c),
                PolyTri(Triangle{x: a, y: b, z: c}) => (a, b, c)
            };

            let a = Vector3::new(a.position[0],
                                 a.position[1],
                                 a.position[2]);
            let b = Vector3::new(b.position[0],
                                 b.position[1],
                                 b.position[2]);
            let c = Vector3::new(c.position[0],
                                 c.position[1],
                                 c.position[2]);

            let normal = (a - b).cross(&(b - c)).normalize();

            match p {
                PolyQuad(Quad{x: mut a, y: mut b, z: mut c, w: mut d}) => {
                    a.normal = [normal.x, normal.y, normal.z];
                    b.normal = [normal.x, normal.y, normal.z];
                    c.normal = [normal.x, normal.y, normal.z];
                    d.normal = [normal.x, normal.y, normal.z];

                    a.texture = [-1., -1.];
                    b.texture = [-1.,  1.];
                    c.texture = [ 1.,  1.];
                    d.texture = [ 1., -1.];

                    PolyQuad(Quad::new(a, b, c, d))
                }
                PolyTri(Triangle{x: mut a, y: mut b, z: mut c}) => {
                    a.normal = [normal.x, normal.y, normal.z];
                    b.normal = [normal.x, normal.y, normal.z];
                    c.normal = [normal.x, normal.y, normal.z];

                    a.texture = [-1., -1.];
                    b.texture = [-1.,  1.];
                    c.texture = [ 1.,  1.];

                    PolyTri(Triangle::new(a, b, c))
                }
            }
        })
        .vertex(|v| indexer.index(v) as u32)
        .triangulate()
        .vertices()
        .collect()
    };

    (mesh_data, index)
}

pub fn load_default(db: &mut Graphics) {
    let core_dir = db.add_dir(None, "core");
    let mat_dir = db.add_dir(Some(core_dir), "material");
    let flat_dir = db.add_dir(Some(mat_dir), "flat");

    for &(name, ref color) in WEB_COLORS.iter() {
        db.new_material(flat_dir, name, Material::simple(*color));
    }

    let geo_dir = db.add_dir(Some(core_dir), "geometry");

    let (cube_v, cube_i) = build_vectors(
        Cube::new().vertex(|(x, y, z)| {
            VertexGeoTexNorm {
                position: [x, y, z],
                texture: [0., 0.],
                normal: [0., 0., 0.]
            }
        }
    ));
    let cube_len = cube_i.len();
    let cube_vb = VertexBuffer::new_position_texture_normal(cube_v, cube_i);
    let cube_vbo = db.new_vertex_buffer(geo_dir, "cube_vbo", cube_vb);
    db.new_geometry(geo_dir, "cube", Geometry::triangles(cube_vbo, 0, cube_len));

    let (plane_v, plane_i) = build_vectors(
        Plane::new().vertex(|(x, y)| {
            VertexGeoTexNorm {
                position: [x, y, 0.],
                texture: [0., 0.],
                normal: [0., 0., 0.]
            }
        }
    ));
    let plane_len = plane_i.len();
    let plane_vb = VertexBuffer::new_position_texture_normal(plane_v, plane_i);
    let plane_vbo = db.new_vertex_buffer(geo_dir, "plane_vbo", plane_vb);
    db.new_geometry(geo_dir, "plane", Geometry::triangles(plane_vbo, 0, plane_len));

    for i in range(2, 8) {
        for j in range(2, 8) {
            let i = pow(2, i);
            let j = pow(2, j);
            let (sphere_v, sphere_i) = build_vectors_poly(
                SphereUV::new(i, j).vertex(|(x, y, z)| {
                    VertexGeoTexNorm {
                        position: [x, y, z],
                        texture: [0., 0.],
                        normal: [0., 0., 0.]
                    }
                }
            ));
            let sphere_len = sphere_i.len();
            let sphere_vb = VertexBuffer::new_position_texture_normal(sphere_v, sphere_i);
            let sphere_vbo = db.new_vertex_buffer(geo_dir,
                format!("sphere_uv_{}_{}_vbo", i, j).as_slice(),
                sphere_vb
            );
            db.new_geometry(geo_dir,
                format!("sphere_uv_{}_{}", i, j).as_slice(),
                Geometry::triangles(sphere_vbo, 0, sphere_len)
            );
        }
    }
}<commit_msg>fix normals in sphere<commit_after>//   Copyright 2014 Colin Sherratt
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

use geometry::{VertexBuffer, Geometry, VertexGeoTexNorm};
use material::Material;
use Graphics;

use genmesh::generators::{Plane, Cube, SphereUV};
use genmesh::{MapToVertices, Indexer, LruIndexer};
use genmesh::{Vertices, Triangulate, Quad, Triangle};
use genmesh::{Polygon, PolyTri, PolyQuad};
use cgmath::{Vector3, EuclideanVector};

use std::num::pow;

static WEB_COLORS: [(&'static str, [f32, ..3]), ..16] = [
    ("white",   [1., 1., 1.]),
    ("silver",  [0.75, 0.75, 0.75]),
    ("gray",    [0.5, 0.5, 0.5]),
    ("black",   [0., 0., 0.]),
    ("red",     [1., 0., 0.]),
    ("maroon",  [0.5, 0., 0.]),
    ("yellow",  [1., 1., 0.]),
    ("olive",   [0.5, 0.5, 0.]),
    ("line",    [0., 1., 0.]),
    ("green",   [0., 0.5, 0.]),
    ("aqua",    [0., 1., 1.]),
    ("teal",    [0., 0.5, 0.5]),
    ("blue",    [0., 0., 1.]),
    ("navy",    [0., 0., 0.5]),
    ("fuchsia", [1., 0., 1.]),
    ("pruple",  [0.5, 0., 0.5]),
];

fn build_vectors<T: Iterator<Quad<VertexGeoTexNorm>>>(input: T)
    -> (Vec<VertexGeoTexNorm>, Vec<u32>) {

    let mut mesh_data: Vec<VertexGeoTexNorm> = Vec::new();
    let mut index: Vec<u32> = {
        let mut indexer = LruIndexer::new(16, |_, v| mesh_data.push(v));
        input.map(|mut p| {
            let a = Vector3::new(p.x.position[0],
                                 p.x.position[1],
                                 p.x.position[2]);
            let b = Vector3::new(p.y.position[0],
                                 p.y.position[1],
                                 p.y.position[2]);
            let c = Vector3::new(p.z.position[0],
                                 p.z.position[1],
                                 p.z.position[2]);

            let normal = (a - b).cross(&(b - c)).normalize();

            p.x.normal = [normal.x, normal.y, normal.z];
            p.y.normal = [normal.x, normal.y, normal.z];
            p.z.normal = [normal.x, normal.y, normal.z];
            p.w.normal = [normal.x, normal.y, normal.z];

            p.x.texture = [-1., -1.];
            p.y.texture = [-1.,  1.];
            p.z.texture = [ 1.,  1.];
            p.w.texture = [ 1., -1.];

            p
        })
        .vertex(|v| indexer.index(v) as u32)
        .triangulate()
        .vertices()
        .collect()
    };

    (mesh_data, index)
}

fn build_vectors_poly<T: Iterator<Polygon<(f32, f32, f32)>>>(input: T)
    -> (Vec<VertexGeoTexNorm>, Vec<u32>) {

    let mut mesh_data: Vec<VertexGeoTexNorm> = Vec::new();
    let mut index: Vec<u32> = {
        let mut indexer = LruIndexer::new(16, |_, v| mesh_data.push(v));
        input
        .vertex(|(x, y, z)| {
            let n = Vector3::new(x, y, z).normalize();
            VertexGeoTexNorm {
                position: [x, y, z],
                texture: [0., 0.],
                normal: [n.x, n.y, n.z]
            }
        })
        .vertex(|v| indexer.index(v) as u32)
        .triangulate()
        .vertices()
        .collect()
    };

    (mesh_data, index)
}

pub fn load_default(db: &mut Graphics) {
    let core_dir = db.add_dir(None, "core");
    let mat_dir = db.add_dir(Some(core_dir), "material");
    let flat_dir = db.add_dir(Some(mat_dir), "flat");

    for &(name, ref color) in WEB_COLORS.iter() {
        db.new_material(flat_dir, name, Material::simple(*color));
    }

    let geo_dir = db.add_dir(Some(core_dir), "geometry");

    let (cube_v, cube_i) = build_vectors(
        Cube::new().vertex(|(x, y, z)| {
            VertexGeoTexNorm {
                position: [x, y, z],
                texture: [0., 0.],
                normal: [0., 0., 0.]
            }
        }
    ));
    let cube_len = cube_i.len();
    let cube_vb = VertexBuffer::new_position_texture_normal(cube_v, cube_i);
    let cube_vbo = db.new_vertex_buffer(geo_dir, "cube_vbo", cube_vb);
    db.new_geometry(geo_dir, "cube", Geometry::triangles(cube_vbo, 0, cube_len));

    let (plane_v, plane_i) = build_vectors(
        Plane::new().vertex(|(x, y)| {
            VertexGeoTexNorm {
                position: [x, y, 0.],
                texture: [0., 0.],
                normal: [0., 0., 0.]
            }
        }
    ));
    let plane_len = plane_i.len();
    let plane_vb = VertexBuffer::new_position_texture_normal(plane_v, plane_i);
    let plane_vbo = db.new_vertex_buffer(geo_dir, "plane_vbo", plane_vb);
    db.new_geometry(geo_dir, "plane", Geometry::triangles(plane_vbo, 0, plane_len));

    for i in range(2, 8) {
        for j in range(2, 8) {
            let i = pow(2, i);
            let j = pow(2, j);
            let (sphere_v, sphere_i) = build_vectors_poly(SphereUV::new(i, j));
            let sphere_len = sphere_i.len();
            let sphere_vb = VertexBuffer::new_position_texture_normal(sphere_v, sphere_i);
            let sphere_vbo = db.new_vertex_buffer(geo_dir,
                format!("sphere_uv_{}_{}_vbo", i, j).as_slice(),
                sphere_vb
            );
            db.new_geometry(geo_dir,
                format!("sphere_uv_{}_{}", i, j).as_slice(),
                Geometry::triangles(sphere_vbo, 0, sphere_len)
            );
        }
    }
}<|endoftext|>


<commit_before>//! Get the system's UTC offset on Unix.

use core::convert::TryInto;
use core::mem::MaybeUninit;

use crate::{OffsetDateTime, UtcOffset};

/// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on any error.
///
/// # Safety
///
/// This method must only be called when the process is single-threaded.
///
/// This method will remain `unsafe` until `std::env::set_var` is deprecated or has its behavior
/// altered. This method is, on its own, safe. It is the presence of a safe, unsound way to set
/// environment variables that makes it unsafe.
unsafe fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
    extern "C" {
        #[cfg_attr(target_os = "netbsd", link_name = "__tzset50")]
        fn tzset();
    }

    // The exact type of `timestamp` beforehand can vary, so this conversion is necessary.
    #[allow(clippy::useless_conversion)]
    let timestamp = timestamp.try_into().ok()?;

    let mut tm = MaybeUninit::uninit();

    // Update timezone information from system. `localtime_r` does not do this for us.
    //
    // Safety: tzset is thread-safe.
    unsafe { tzset() };

    // Safety: We are calling a system API, which mutates the `tm` variable. If a null
    // pointer is returned, an error occurred.
    let tm_ptr = unsafe { libc::localtime_r(&timestamp, tm.as_mut_ptr()) };

    if tm_ptr.is_null() {
        None
    } else {
        // Safety: The value was initialized, as we no longer have a null pointer.
        Some(unsafe { tm.assume_init() })
    }
}

/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
// `tm_gmtoff` extension
#[cfg(not(all(
    unsound_local_offset,
    any(target_os = "solaris", target_os = "illumos")
)))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
    let seconds: i32 = tm.tm_gmtoff.try_into().ok()?;
    UtcOffset::from_hms(
        (seconds / 3_600) as _,
        ((seconds / 60) % 60) as _,
        (seconds % 60) as _,
    )
    .ok()
}

/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
#[cfg(all(
    unsound_local_offset,
    any(target_os = "solaris", target_os = "illumos")
))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
    use core::convert::TryFrom;

    use crate::Date;

    let mut tm = tm;
    if tm.tm_sec == 60 {
        // Leap seconds are not currently supported.
        tm.tm_sec = 59;
    }

    let local_timestamp =
        Date::from_ordinal_date(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
            .ok()?
            .with_hms(
                tm.tm_hour.try_into().ok()?,
                tm.tm_min.try_into().ok()?,
                tm.tm_sec.try_into().ok()?,
            )
            .ok()?
            .assume_utc()
            .unix_timestamp();

    let diff_secs: i32 = (local_timestamp - datetime.unix_timestamp())
        .try_into()
        .ok()?;

    UtcOffset::from_hms(
        (diff_secs / 3_600) as _,
        ((diff_secs / 60) % 60) as _,
        (diff_secs % 60) as _,
    )
    .ok()
}

/// Obtain the system's UTC offset.
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
    // Ensure that the process is single-threaded unless the user has explicitly opted out of this
    // check. This is to prevent issues with the environment being mutated by a different thread in
    // the process while execution of this function is taking place, which can cause a segmentation
    // fault by dereferencing a dangling pointer.
    // If the `num_threads` crate is incapable of determining the number of running threads, then
    // we conservatively return `None` to avoid a soundness bug.
    if !cfg!(unsound_local_offset) && num_threads::is_single_threaded() != Some(true) {
        return None;
    }

    // Safety: We have just confirmed that the process is single-threaded or the user has explicitly
    // opted out of soundness.
    let tm = unsafe { timestamp_to_tm(datetime.unix_timestamp()) }?;
    tm_to_offset(tm)
}
<commit_msg>Fix Solaris/Illumos build<commit_after>//! Get the system's UTC offset on Unix.

use core::convert::TryInto;
use core::mem::MaybeUninit;

use crate::{OffsetDateTime, UtcOffset};

/// Convert the given Unix timestamp to a `libc::tm`. Returns `None` on any error.
///
/// # Safety
///
/// This method must only be called when the process is single-threaded.
///
/// This method will remain `unsafe` until `std::env::set_var` is deprecated or has its behavior
/// altered. This method is, on its own, safe. It is the presence of a safe, unsound way to set
/// environment variables that makes it unsafe.
unsafe fn timestamp_to_tm(timestamp: i64) -> Option<libc::tm> {
    extern "C" {
        #[cfg_attr(target_os = "netbsd", link_name = "__tzset50")]
        fn tzset();
    }

    // The exact type of `timestamp` beforehand can vary, so this conversion is necessary.
    #[allow(clippy::useless_conversion)]
    let timestamp = timestamp.try_into().ok()?;

    let mut tm = MaybeUninit::uninit();

    // Update timezone information from system. `localtime_r` does not do this for us.
    //
    // Safety: tzset is thread-safe.
    unsafe { tzset() };

    // Safety: We are calling a system API, which mutates the `tm` variable. If a null
    // pointer is returned, an error occurred.
    let tm_ptr = unsafe { libc::localtime_r(&timestamp, tm.as_mut_ptr()) };

    if tm_ptr.is_null() {
        None
    } else {
        // Safety: The value was initialized, as we no longer have a null pointer.
        Some(unsafe { tm.assume_init() })
    }
}

/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
// `tm_gmtoff` extension
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
    let seconds: i32 = tm.tm_gmtoff.try_into().ok()?;
    UtcOffset::from_hms(
        (seconds / 3_600) as _,
        ((seconds / 60) % 60) as _,
        (seconds % 60) as _,
    )
    .ok()
}

/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
// Solaris/Illumos is unsound and requires opting into.
#[cfg(all(
    not(unsound_local_offset),
    any(target_os = "solaris", target_os = "illumos")
))]
#[allow(unused_variables, clippy::missing_const_for_fn)]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
    None
}

/// Convert a `libc::tm` to a `UtcOffset`. Returns `None` on any error.
#[cfg(all(
    unsound_local_offset,
    any(target_os = "solaris", target_os = "illumos")
))]
fn tm_to_offset(tm: libc::tm) -> Option<UtcOffset> {
    use core::convert::TryFrom;

    use crate::Date;

    let mut tm = tm;
    if tm.tm_sec == 60 {
        // Leap seconds are not currently supported.
        tm.tm_sec = 59;
    }

    let local_timestamp =
        Date::from_ordinal_date(1900 + tm.tm_year, u16::try_from(tm.tm_yday).ok()? + 1)
            .ok()?
            .with_hms(
                tm.tm_hour.try_into().ok()?,
                tm.tm_min.try_into().ok()?,
                tm.tm_sec.try_into().ok()?,
            )
            .ok()?
            .assume_utc()
            .unix_timestamp();

    let diff_secs: i32 = (local_timestamp - datetime.unix_timestamp())
        .try_into()
        .ok()?;

    UtcOffset::from_hms(
        (diff_secs / 3_600) as _,
        ((diff_secs / 60) % 60) as _,
        (diff_secs % 60) as _,
    )
    .ok()
}

/// Obtain the system's UTC offset.
pub(super) fn local_offset_at(datetime: OffsetDateTime) -> Option<UtcOffset> {
    // Ensure that the process is single-threaded unless the user has explicitly opted out of this
    // check. This is to prevent issues with the environment being mutated by a different thread in
    // the process while execution of this function is taking place, which can cause a segmentation
    // fault by dereferencing a dangling pointer.
    // If the `num_threads` crate is incapable of determining the number of running threads, then
    // we conservatively return `None` to avoid a soundness bug.
    if !cfg!(unsound_local_offset) && num_threads::is_single_threaded() != Some(true) {
        return None;
    }

    // Safety: We have just confirmed that the process is single-threaded or the user has explicitly
    // opted out of soundness.
    let tm = unsafe { timestamp_to_tm(datetime.unix_timestamp()) }?;
    tm_to_offset(tm)
}
<|endoftext|>


<commit_before>// Issue #3656
// Incorrect struct size computation in the FFI, because of not taking
// the alignment of elements into account.

use libc::*;

struct KEYGEN {
    hash_algorithm: [c_uint]/2,
    count: uint32_t,
    salt: *c_void,
    salt_size: uint32_t,
}

extern {
    // Bogus signature, just need to test if it compiles.
    pub fn malloc(++data: KEYGEN);
}

fn main() {
}
<commit_msg>Remove remaining uses of old fixed-length vec syntax<commit_after>// Issue #3656
// Incorrect struct size computation in the FFI, because of not taking
// the alignment of elements into account.

use libc::*;

struct KEYGEN {
    hash_algorithm: [c_uint * 2],
    count: uint32_t,
    salt: *c_void,
    salt_size: uint32_t,
}

extern {
    // Bogus signature, just need to test if it compiles.
    pub fn malloc(++data: KEYGEN);
}

fn main() {
}
<|endoftext|>


<commit_before>// xfail-boot
fn main() {

  obj foo() {
      impure fn m1(mutable int i) -> int {
          i += 1;
          ret i;
      }
      impure fn m2(mutable int i) -> int {
          ret self.m1(i);
      }
      impure fn m3(mutable int i) -> int {
          i += 1;
          ret self.m1(i);
      }
  }

  auto a = foo();
  let int i = 0;

  // output should be: 0, 1, 2, 4
  log i;
  i = a.m1(i);
  log i;
  i = a.m2(i);
  log i;
  i = a.m3(i);
  log i;
}

<commit_msg>Updated test to use 'check' rather than just logging results.<commit_after>// xfail-boot
fn main() {

  obj foo() {
      impure fn m1(mutable int i) -> int {
          i += 1;
          ret i;
      }
      impure fn m2(mutable int i) -> int {
          ret self.m1(i);
      }
      impure fn m3(mutable int i) -> int {
          i += 1;
          ret self.m1(i);
      }
  }

  auto a = foo();
  let int i = 0;

  i = a.m1(i);
  check (i == 1);
  i = a.m2(i);
  check (i == 2);
  i = a.m3(i);
  check (i == 4);
}

<|endoftext|>


<commit_before>#![cfg(feature = "support")]

extern crate rusoto_core;
extern crate rusoto_support;

use rusoto_support::{AWSSupport, AWSSupportClient, DescribeServicesRequest};
use rusoto_core::{DefaultCredentialsProvider, Region};
use rusoto_core::default_tls_client;

#[test]
fn should_list_domains() {
    let credentials = DefaultCredentialsProvider::new().unwrap();
    let client = AWSSupportClient::new(default_tls_client().unwrap(), credentials, Region::UsEast1);

    let request = DescribeServicesRequest::default();

    let result = client.describe_services(&request).unwrap();
    println!("{:#?}", result);
}
<commit_msg>Remove AWS Support integration test<commit_after><|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::fs::OpenOptions;
use std::path::PathBuf;

use serde_json;

use devicemapper::{devnode_to_devno, Device, Sectors};

use stratis::{ErrorEnum, StratisError, StratisResult};

use super::super::super::types::{BlockDevTier, DevUuid, PoolUuid};

use super::super::serde_structs::{BackstoreSave, BlockDevSave, PoolSave};

use super::blockdev::StratBlockDev;
use super::device::{blkdev_size, is_stratis_device};
use super::metadata::BDA;
use super::util::get_stratis_block_devices;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                is_stratis_device(&devnode)?.and_then(|pool_uuid| {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode)
                });
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
#[allow(implicit_hasher)]
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<PoolSave>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas.iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in bdas.iter()
        .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time))
    {
        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data successfully read";
    Err(StratisError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices.
/// Returns an error if a BDA can not be read or can not be found on any
/// blockdev in devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
/// Precondition: Every device in devnodes has already been determined to
/// belong to the pool with the specified pool uuid.
#[allow(implicit_hasher)]
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<DevUuid, &BlockDevSave> = backstore_save
        .data_devs
        .iter()
        .map(|bds| (bds.uuid, bds))
        .collect();

    let recorded_cache_map: HashMap<DevUuid, &BlockDevSave> = match backstore_save.cache_devs {
        Some(ref cache_devs) => cache_devs.iter().map(|bds| (bds.uuid, bds)).collect(),
        None => HashMap::new(),
    };

    let mut segment_table: HashMap<DevUuid, Vec<(Sectors, Sectors)>> = HashMap::new();
    for seg in &backstore_save.data_segments {
        segment_table
            .entry(seg.0)
            .or_insert_with(Vec::default)
            .push((seg.1, seg.2))
    }
    if let Some(ref segs) = backstore_save.cache_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }
    if let Some(ref segs) = backstore_save.meta_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }

    let (mut datadevs, mut cachedevs) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?.ok_or_else(|| {
            StratisError::Engine(ErrorEnum::NotFound,
                                                 format!("Device {} with devnode {} was previously determined to belong to pool with uuid {} but no BDA was found",
                                                 device,
                                                 devnode.display(),
                                                 pool_uuid))
        })?;

        // Return an error if apparent size of Stratis block device appears to
        // have decreased since metadata was recorded or if size of block
        // device could not be obtained.
        blkdev_size(&OpenOptions::new().read(true).open(devnode)?).and_then(|actual_size| {
            let actual_size_sectors = actual_size.sectors();
            let recorded_size = bda.dev_size();
            if actual_size_sectors < recorded_size {
                let err_msg = format!(
                    "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had recorded size ({}), but actual size is less at ({})",
                    device,
                    devnode.display(),
                    bda.pool_uuid(),
                    bda.dev_uuid(),
                    recorded_size,
                    actual_size
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            } else {
                Ok(())
            }
        })?;

        let dev_uuid = bda.dev_uuid();

        // Locate the device in the metadata using its uuid. Return the device
        // metadata and whether it was a cache or a datadev.
        let (tier, bd_save) = recorded_data_map
            .get(&dev_uuid)
            .map(|bd_save| (BlockDevTier::Data, bd_save))
            .or_else(|| {
                recorded_cache_map
                    .get(&dev_uuid)
                    .map(|bd_save| (BlockDevTier::Cache, bd_save))
            })
            .ok_or_else(|| {
                let err_msg = format!(
                        "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had no record in pool metadata",
                        device,
                        devnode.display(),
                        bda.pool_uuid(),
                        bda.dev_uuid()
                    );
                StratisError::Engine(ErrorEnum::NotFound, err_msg)
            })?;

        // This should always succeed since the actual size is at
        // least the recorded size, so all segments should be
        // available to be allocated. If this fails, the most likely
        // conclusion is metadata corruption.
        let segments = segment_table.get(&dev_uuid);
        match tier {
            BlockDevTier::Data => &mut datadevs,
            BlockDevTier::Cache => &mut cachedevs,
        }.push(StratBlockDev::new(
            *device,
            devnode.to_owned(),
            bda,
            segments.unwrap_or(&vec![]),
            bd_save.user_info.clone(),
            bd_save.hardware_info.clone(),
        )?);
    }

    // Verify that datadevs found match datadevs recorded.
    let current_data_uuids: HashSet<_> = datadevs.iter().map(|b| b.uuid()).collect();
    let recorded_data_uuids: HashSet<_> = recorded_data_map.keys().cloned().collect();
    if current_data_uuids != recorded_data_uuids {
        let err_msg = "Recorded data dev UUIDs != discovered datadev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if datadevs.len() != current_data_uuids.len() {
        let err_msg = "Duplicate data devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    // Verify that cachedevs found match cachedevs recorded.
    let current_cache_uuids: HashSet<_> = cachedevs.iter().map(|b| b.uuid()).collect();
    let recorded_cache_uuids: HashSet<_> = recorded_cache_map.keys().cloned().collect();
    if current_cache_uuids != recorded_cache_uuids {
        let err_msg = "Recorded cache dev UUIDs != discovered cachedev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if cachedevs.len() != current_cache_uuids.len() {
        let err_msg = "Duplicate cache devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok((datadevs, cachedevs))
}
<commit_msg>Add an index to the data stored by the data maps<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

// Code to handle initial setup steps for a pool.
// Initial setup steps are steps that do not alter the environment.

use std::collections::{HashMap, HashSet};
use std::fs::OpenOptions;
use std::path::PathBuf;

use serde_json;

use devicemapper::{devnode_to_devno, Device, Sectors};

use stratis::{ErrorEnum, StratisError, StratisResult};

use super::super::super::types::{BlockDevTier, DevUuid, PoolUuid};

use super::super::serde_structs::{BackstoreSave, BlockDevSave, PoolSave};

use super::blockdev::StratBlockDev;
use super::device::{blkdev_size, is_stratis_device};
use super::metadata::BDA;
use super::util::get_stratis_block_devices;

/// Find all Stratis devices.
///
/// Returns a map of pool uuids to a map of devices to devnodes for each pool.
pub fn find_all() -> StratisResult<HashMap<PoolUuid, HashMap<Device, PathBuf>>> {
    let mut pool_map = HashMap::new();

    for devnode in get_stratis_block_devices()? {
        match devnode_to_devno(&devnode)? {
            None => continue,
            Some(devno) => {
                is_stratis_device(&devnode)?.and_then(|pool_uuid| {
                    pool_map
                        .entry(pool_uuid)
                        .or_insert_with(HashMap::new)
                        .insert(Device::from(devno), devnode)
                });
            }
        }
    }
    Ok(pool_map)
}

/// Get the most recent metadata from a set of Devices for a given pool UUID.
/// Returns None if no metadata found for this pool.
#[allow(implicit_hasher)]
pub fn get_metadata(
    pool_uuid: PoolUuid,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<Option<PoolSave>> {
    // Get pairs of device nodes and matching BDAs
    // If no BDA, or BDA UUID does not match pool UUID, skip.
    // If there is an error reading the BDA, error. There could have been
    // vital information on that BDA, for example, it may have contained
    // the newest metadata.
    let mut bdas = Vec::new();
    for devnode in devnodes.values() {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?;
        if let Some(bda) = bda {
            if bda.pool_uuid() == pool_uuid {
                bdas.push((devnode, bda));
            }
        }
    }

    // Most recent time should never be None if this was a properly
    // created pool; this allows for the method to be called in other
    // circumstances.
    let most_recent_time = {
        match bdas.iter()
            .filter_map(|&(_, ref bda)| bda.last_update_time())
            .max()
        {
            Some(time) => time,
            None => return Ok(None),
        }
    };

    // Try to read from all available devnodes that could contain most
    // recent metadata. In the event of errors, continue to try until all are
    // exhausted.
    for &(devnode, ref bda) in bdas.iter()
        .filter(|&&(_, ref bda)| bda.last_update_time() == Some(most_recent_time))
    {
        let poolsave = OpenOptions::new()
            .read(true)
            .open(devnode)
            .ok()
            .and_then(|mut f| bda.load_state(&mut f).ok())
            .and_then(|opt| opt)
            .and_then(|data| serde_json::from_slice(&data).ok());

        if poolsave.is_some() {
            return Ok(poolsave);
        }
    }

    // If no data has yet returned, we have an error. That is, we should have
    // some metadata, because we have a most recent time, but we failed to
    // get any.
    let err_str = "timestamp indicates data was written, but no data successfully read";
    Err(StratisError::Engine(ErrorEnum::NotFound, err_str.into()))
}

/// Get all the blockdevs corresponding to this pool that can be obtained from
/// the given devices.
/// Returns an error if a BDA can not be read or can not be found on any
/// blockdev in devnodes.
/// Returns an error if the blockdevs obtained do not match the metadata.
/// Returns a tuple, of which the first are the data devs, and the second
/// are the devs that support the cache tier.
/// Precondition: Every device in devnodes has already been determined to
/// belong to the pool with the specified pool uuid.
#[allow(implicit_hasher)]
pub fn get_blockdevs(
    pool_uuid: PoolUuid,
    backstore_save: &BackstoreSave,
    devnodes: &HashMap<Device, PathBuf>,
) -> StratisResult<(Vec<StratBlockDev>, Vec<StratBlockDev>)> {
    let recorded_data_map: HashMap<DevUuid, (usize, &BlockDevSave)> = backstore_save
        .data_devs
        .iter()
        .enumerate()
        .map(|(i, bds)| (bds.uuid, (i, bds)))
        .collect();

    let recorded_cache_map: HashMap<DevUuid, (usize, &BlockDevSave)> =
        match backstore_save.cache_devs {
            Some(ref cache_devs) => cache_devs
                .iter()
                .enumerate()
                .map(|(i, bds)| (bds.uuid, (i, bds)))
                .collect(),
            None => HashMap::new(),
        };

    let mut segment_table: HashMap<DevUuid, Vec<(Sectors, Sectors)>> = HashMap::new();
    for seg in &backstore_save.data_segments {
        segment_table
            .entry(seg.0)
            .or_insert_with(Vec::default)
            .push((seg.1, seg.2))
    }
    if let Some(ref segs) = backstore_save.cache_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }
    if let Some(ref segs) = backstore_save.meta_segments {
        for seg in segs {
            segment_table
                .entry(seg.0)
                .or_insert_with(Vec::default)
                .push((seg.1, seg.2))
        }
    }

    let (mut datadevs, mut cachedevs) = (vec![], vec![]);
    for (device, devnode) in devnodes {
        let bda = BDA::load(&mut OpenOptions::new().read(true).open(devnode)?)?.ok_or_else(|| {
            StratisError::Engine(ErrorEnum::NotFound,
                                                 format!("Device {} with devnode {} was previously determined to belong to pool with uuid {} but no BDA was found",
                                                 device,
                                                 devnode.display(),
                                                 pool_uuid))
        })?;

        // Return an error if apparent size of Stratis block device appears to
        // have decreased since metadata was recorded or if size of block
        // device could not be obtained.
        blkdev_size(&OpenOptions::new().read(true).open(devnode)?).and_then(|actual_size| {
            let actual_size_sectors = actual_size.sectors();
            let recorded_size = bda.dev_size();
            if actual_size_sectors < recorded_size {
                let err_msg = format!(
                    "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had recorded size ({}), but actual size is less at ({})",
                    device,
                    devnode.display(),
                    bda.pool_uuid(),
                    bda.dev_uuid(),
                    recorded_size,
                    actual_size
                );
                Err(StratisError::Engine(ErrorEnum::Error, err_msg))
            } else {
                Ok(())
            }
        })?;

        let dev_uuid = bda.dev_uuid();

        // Locate the device in the metadata using its uuid. Return the device
        // metadata and whether it was a cache or a datadev.
        let (tier, (_, bd_save)) = recorded_data_map
            .get(&dev_uuid)
            .map(|bd_save| (BlockDevTier::Data, bd_save))
            .or_else(|| {
                recorded_cache_map
                    .get(&dev_uuid)
                    .map(|bd_save| (BlockDevTier::Cache, bd_save))
            })
            .ok_or_else(|| {
                let err_msg = format!(
                        "Stratis device with device number {}, devnode {}, pool UUID {} and device UUID {} had no record in pool metadata",
                        device,
                        devnode.display(),
                        bda.pool_uuid(),
                        bda.dev_uuid()
                    );
                StratisError::Engine(ErrorEnum::NotFound, err_msg)
            })?;

        // This should always succeed since the actual size is at
        // least the recorded size, so all segments should be
        // available to be allocated. If this fails, the most likely
        // conclusion is metadata corruption.
        let segments = segment_table.get(&dev_uuid);
        match tier {
            BlockDevTier::Data => &mut datadevs,
            BlockDevTier::Cache => &mut cachedevs,
        }.push(StratBlockDev::new(
            *device,
            devnode.to_owned(),
            bda,
            segments.unwrap_or(&vec![]),
            bd_save.user_info.clone(),
            bd_save.hardware_info.clone(),
        )?);
    }

    // Verify that datadevs found match datadevs recorded.
    let current_data_uuids: HashSet<_> = datadevs.iter().map(|b| b.uuid()).collect();
    let recorded_data_uuids: HashSet<_> = recorded_data_map.keys().cloned().collect();
    if current_data_uuids != recorded_data_uuids {
        let err_msg = "Recorded data dev UUIDs != discovered datadev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if datadevs.len() != current_data_uuids.len() {
        let err_msg = "Duplicate data devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    // Verify that cachedevs found match cachedevs recorded.
    let current_cache_uuids: HashSet<_> = cachedevs.iter().map(|b| b.uuid()).collect();
    let recorded_cache_uuids: HashSet<_> = recorded_cache_map.keys().cloned().collect();
    if current_cache_uuids != recorded_cache_uuids {
        let err_msg = "Recorded cache dev UUIDs != discovered cachedev UUIDs";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    if cachedevs.len() != current_cache_uuids.len() {
        let err_msg = "Duplicate cache devices found in environment";
        return Err(StratisError::Engine(ErrorEnum::Invalid, err_msg.into()));
    }

    Ok((datadevs, cachedevs))
}
<|endoftext|>


<commit_before>use ::{DeterministicRandomId, PersistentTypeId, ListedType, PersistentlyIdentifiedType, SimulationStateData, DataHandleTrait, DataTimelineCellTrait, ExtendedTime};
use ::stewards::{simple_full as steward_module};
use self::steward_module::{TimeSteward, Event, DataTimelineCellReadGuard, DataHandle, DataTimelineCell, Accessor, EventAccessor, FutureCleanupAccessor, SnapshotAccessor, simple_timeline};
use self::simple_timeline::{SimpleTimeline, GetVarying, tracking_query, tracking_query_ref, set, unset};

pub type Coordinate = u32;
pub type NumDimensions = u32;

/// If there's only one interpretation of how your objects are arranged in space, it makes sense to implement this on a unit-like struct.
pub trait Space: SimulationStateData + PersistentlyIdentifiedType {
  type Steward: TimeSteward;
  type Object: SimulationStateData + PersistentlyIdentifiedType;
  type DetectorDataPerObject: SimulationStateData;

  const DIMENSIONS: NumDimensions;

  // An Object generally has to store some opaque data for the collision detector.
  // It would normally include a DataHandle to a tree node.
  // These are getter and setter methods for that data.
  fn get_detector_data<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>)->Option<&DetectorDataPerObject<Self>>>;
  fn set_detector_data<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>, data: Option<DetectorDataPerObject<Self>>);

  fn current_bounding_box<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>)->BoundingBox;
  fn when_escapes<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>, space: &Self::Space, BoundingBox)-><Self::Steward as TimeSteward>::Basics::Time;

  fn become_neighbors<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, objects: [&DataHandle<Self::Object>; 2]) {}
  fn stop_being_neighbors<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, objects: [&DataHandle<Self::Object>; 2]) {}
}

pub trait Detector: SimulationStateData {
  type Steward: TimeSteward;
  type Object: SimulationStateData + PersistentlyIdentifiedType;
  type DetectorDataPerObject: SimulationStateData;

  pub fn insert<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>, location_hint: Option <& DataHandle<B::Object>>);
  pub fn remove<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>);
  pub fn changed<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>);
  pub fn nearby_objects<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>) ->Vec<DataHandle<S::Object>>;
}

pub struct BoundingBox<B: BoundingBoxCollisionDetectable> {
  pub bounds: [[Coordinate; 2]; B::DIMENSIONS],
}

pub mod simple_grid {
  use super::*;

  pub struct SimpleGridDetector <Steward: TimeSteward> {
    cell_size: Coordinate,
    cells: DataTimelineCell<SimpleTimeline<HashMap <[Coordinate; DIMENSIONS], Cell>>>,
  }

  struct DetectorDataPerObject {
    current_grid_bounds: BoundingBox,
    escapes_bounds_prediction: Option <<Steward as TimeSteward>::EventHandle>,
  }
  struct Cell {
    objects: Vec<Object>,
  }

  impl Detector for SimpleGridDetector {
    type Steward = Steward;
    type Object = Object;
    type DetectorDataPerObject = DetectorDataPerObject;

    insert (accessor, space, object, location_hint) {

    }

    changed (accessor, space, object) {
      let data = match space.get_detector_data (accessor, object) {None => return, Some (a) => a};
      let new_bounds = space.current_bounding_box (accessor, object);
      let new_grid_bounds = self.grid_box (new_bounds);

      let mut cells = get (accessor, self.cells);
      let mut new_neighbors =
      let mut removed_neighbors =

      for location in new_bounds.locations () {
        if let Some(cell) = cells.get_mut (location) {
          for neighbor in cell.objects {
            new_neighbors.insert (neighbor) ;
          }
          if !old_bounds.contains (location) {
            cell.objects.insert (object.clone());
          }
        }
      }
      for location in old_bounds.locations () {
        if !new_bounds.contains (location) {
          let cell = cells.entry (location).or_insert (Default::default());
          for neighbor in cell.objects {
            if !new_neighbors.contains (neighbor) {
              removed_neighbors.insert (neighbor);
            }
          }
          cell.objects.insert (object.clone());
        }
      }

      for neighbor in removed_neighbors {
        space.stop_being_neighbors (accessor, [object, neighbor]);
      }
      for neighbor in new_neighbors {
        space.become_neighbors (accessor, [object, neighbor]);
      }

    }
  }

  impl SimpleGridDetector {
    fn grid_box (&self, exact_box: & BoundingBox) -> BoundingBox {
      Array::from_fn (| dimension | Array::from_fn (| direction | {
        exact_box [dimension] [direction] + direction*(self.cell_size - 1)/self.cell_size
      }))
    }
    fn real_box_from_grid (&self, grid_box: & BoundingBox) -> BoundingBox {
      Array::from_fn (| dimension | Array::from_fn (| direction | {
        grid_box [dimension] [direction] * self.cell_size
      }))
    }
  }
}

/*
struct NodeBounds<B: BoundingBoxCollisionDetectable> {
  half_size_shift: u32,
  center: [Coordinate; B::DIMENSIONS],
}
fn smallest_containing_node_bounds <B: BoundingBoxCollisionDetectable> (bounds: & BoundingBox<B>)->NodeBounds <B> {
  unimplemented!()
}

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
struct Node<B: BoundingBoxCollisionDetectable> {
  bounds: NodeBounds<B>,
  parent: Option<DataHandle <Node <B>>>,
  larger_cousins: [[Option<DataHandle <Node <B>>>; 2]; B::DIMENSIONS],
  varying: NodeVarying <B>,
}
impl<B: BoundingBoxCollisionDetectable> PersistentlyIdentifiedType for Node<B> {
  const ID: PersistentTypeId = PersistentTypeId(B::ID.0 ^ 0x7c4c8993671ba023);
}
#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
struct NodeVarying<B: BoundingBoxCollisionDetectable> {
  children: [Option<DataHandle <Node <B>>>; 1<<B::DIMENSIONS],
  overlapping_live_nodes: Vec<DataHandle <B::Object>>,
  objects: Vec<DataHandle <B::Object>>,
}

pub struct ObjectData<B: BoundingBoxCollisionDetectable> {
  varying: ObjectVarying<B>,
}
struct ObjectVarying<B: BoundingBoxCollisionDetectable> {
  node: DataHandle <Node <B>>,
}
pub struct BoundingBoxCollisionDetector<B: BoundingBoxCollisionDetectable> {
  root: DataHandle <Node <B>>,
}

impl<B: BoundingBoxCollisionDetectable> BoundingBoxCollisionDetector<B> {
  pub fn insert<A: EventAccessor <Steward = B::Steward>>(accessor: &A, object: &DataHandle<B::Object>, space: &B::Space, location_hint: Option <& DataHandle<B::Object>>) {

  }
  pub fn remove<A: EventAccessor <Steward = B::Steward>>(accessor: &A, object: &DataHandle<B::Object>, space: &B::Space) {

  }
  pub fn neighbors<A: EventAccessor <Steward = B::Steward>>(accessor: &A, object: &DataHandle<B::Object>, space: &B::Space) -> Iter {

  }
}

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct EscapesBounds<B: BoundingBoxCollisionDetectable> {}
impl<B: BoundingBoxCollisionDetectable> PersistentlyIdentifiedType for EscapesBounds<B> {
  const ID: PersistentTypeId = PersistentTypeId(B::ID.0 ^ 0xb0cdbe951b688b70);
}
impl<B: BoundingBoxCollisionDetectable> Event for EscapesBounds<B> {
  type Steward = B::Steward;
  type ExecutionData = ();
  fn execute <Accessor: EventAccessor <Steward = Self::Steward>> (&self, accessor: &mut Accessor) {

  }

  fn undo <Accessor: FutureCleanupAccessor <Steward = Self::Steward>> (&self, accessor: &mut Accessor, _: ()) {
    unimplemented!()
  }
}

impl<B: BoundingBoxCollisionDetectable> BoundingBoxCollisionDetector<B> {
  fn reposition <A: EventAccessor <Steward = B::Steward>> (accessor: &A, object: &DataHandle<B::Object>) {
    let varying = tracking_query (accessor, object.varying);
    let mut current_node = varying.node;
    let new_bounds = B::calculate_current_bounding_box (object);
    let destination_node_bounds = smallest_containing_node_bounds (new_bounds) ;
    let bigger_shift = max (current_node.bounds.half_size_shift, destination_node_bounds.half_size_shift);

    while current_node.bounds.half_size_shift < bigger_shift {
      current_node = current_node.parent.expect("All nodes except the root node have to have a parent, and we should never be trying to navigate to a node bigger than the root");
    }

    let mut destination_ancestor_bounds = destination_node_bounds;
    while destination_ancestor_bounds.half_size_shift < bigger_shift {
      destination_ancestor_bounds = parent_bounds(destination_ancestor_bounds);
    }

    while largest_single_dimension_distance (current_node.bounds, destination_ancestor_bounds) > (1 << destination_ancestor_bounds.half_size_shift) {
      destination_ancestor_bounds = parent_bounds(destination_ancestor_bounds);
      current_node = current_node.parent.expect("All nodes except the root node have to have a parent, and we should never be trying to navigate to a node bigger than the root");
    }

    if current_node.bounds != destination_ancestor_bounds {
      // they should now be the same size and half- or all-overlapping in each dimension.
    }
  }
}

*/
<commit_msg>"finished" simple grid collision detector<commit_after>use ::{DeterministicRandomId, PersistentTypeId, ListedType, PersistentlyIdentifiedType, SimulationStateData, DataHandleTrait, DataTimelineCellTrait, ExtendedTime};
use ::stewards::{simple_full as steward_module};
use self::steward_module::{TimeSteward, Event, DataTimelineCellReadGuard, DataHandle, DataTimelineCell, Accessor, EventAccessor, FutureCleanupAccessor, SnapshotAccessor, simple_timeline};
use self::simple_timeline::{SimpleTimeline, GetVarying, tracking_query, tracking_query_ref, set, unset};

pub type Coordinate = u32;
pub type NumDimensions = u32;

/// If there's only one interpretation of how your objects are arranged in space, it makes sense to implement this on a unit-like struct.
pub trait Space: SimulationStateData + PersistentlyIdentifiedType {
  type Steward: TimeSteward;
  type Object: SimulationStateData + PersistentlyIdentifiedType;
  type DetectorDataPerObject: SimulationStateData;

  const DIMENSIONS: NumDimensions;

  // An Object generally has to store some opaque data for the collision detector.
  // It would normally include a DataHandle to a tree node.
  // These are getter and setter methods for that data.
  fn get_detector_data<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>)->Option<&DetectorDataPerObject<Self>>>;
  fn set_detector_data<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>, data: Option<DetectorDataPerObject<Self>>);

  fn current_bounding_box<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>)->BoundingBox;
  fn when_escapes<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, object: &DataHandle<Self::Object>, space: &Self::Space, BoundingBox)-><Self::Steward as TimeSteward>::Basics::Time;

  fn become_neighbors<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, objects: [&DataHandle<Self::Object>; 2]) {}
  fn stop_being_neighbors<A: EventAccessor <Steward = Self::Steward>>(&self, accessor: &A, objects: [&DataHandle<Self::Object>; 2]) {}
}

pub trait Detector: SimulationStateData {
  type Steward: TimeSteward;
  type Object: SimulationStateData + PersistentlyIdentifiedType;
  type DetectorDataPerObject: SimulationStateData;

  fn insert<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>, location_hint: Option <& DataHandle<B::Object>>);
  fn remove<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>);
  fn changed<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>);
  fn nearby_objects<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>) ->Vec<DataHandle<S::Object>>;
}

pub struct BoundingBox<B: BoundingBoxCollisionDetectable> {
  pub bounds: [[Coordinate; 2]; B::DIMENSIONS],
}

impl BoundingBox {
  pub fn locations (&self)->Vec<[Coordinate; DIMENSIONS]> {
    let mut result = Vec::new() ;
    let mut position = Array::map (self.bounds, | [first, _] | first);
    'outer: loop {
      for dimension in 0..DIMENSIONS {
        let mut dimension = 0;
        position [dimension] += 1;
        if position [dimension] <= self.bounds[dimension] [1] {
          result.push (result);
          continue 'outer;
        }
        position [dimension] = self.bounds[dimension] [0];
      }
      break 'outer;
    }
    result
  }

  pub fn contains_location (&self, location: [Coordinate; DIMENSIONS])->bool {
    self.bounds.iter().enumerate().all (| (dimension, [first, second]) | location [dimension] >= first && location [dimension] <= second))
  }
}

pub mod simple_grid {
  use super::*;

  pub struct SimpleGridDetector <Steward: TimeSteward> {
    cell_size: Coordinate,
    cells: DataTimelineCell<SimpleTimeline<HashMap <[Coordinate; DIMENSIONS], Cell>>>,
  }

  struct DetectorDataPerObject {
    current_grid_bounds: BoundingBox,
    escapes_bounds_prediction: Option <<Steward as TimeSteward>::EventHandle>,
  }
  struct Cell {
    objects: Vec<Object>,
  }

  impl Detector for SimpleGridDetector {
    type Steward = Steward;
    type Object = Object;
    type DetectorDataPerObject = DetectorDataPerObject;

    insert (accessor, space, object, location_hint) {
      let new_bounds = space.current_bounding_box (accessor, object);
      let new_grid_bounds = self.grid_box (new_bounds);
      self.update(accessor, space, object, Some());
    }
    remove (accessor, space, object, location_hint) {
      self.update(accessor, space, object, None);
    }

    changed (accessor, space, object) {
      self.insert(accessor, space, object, None);
    }

    fn nearby_objects<A: EventAccessor <Steward = Self::Steward>, S: Space <DetectorDataPerObject = Self::DetectorDataPerObject>>(accessor: &A, space: &S, object: &DataHandle<S::Object>) ->Vec<DataHandle<S::Object>> {
      let data = match space.get_detector_data (accessor, object) {None => return, Some (a) => a};
      let mut result =;
      for location in data.current_grid_bounds.locations () {
        if let Some(cell) = cells.get (location) {
          for neighbor in cell.objects {
            result.insert (neighbor);
          }
        }
      }
      result
    }
  }

  impl SimpleGridDetector {
    fn grid_box (&self, exact_box: & BoundingBox) -> BoundingBox {
      Array::from_fn (| dimension | Array::from_fn (| direction | {
        exact_box [dimension] [direction] + direction*(self.cell_size - 1)/self.cell_size
      }))
    }
    fn real_box_from_grid (&self, grid_box: & BoundingBox) -> BoundingBox {
      Array::from_fn (| dimension | Array::from_fn (| direction | {
        grid_box [dimension] [direction] * self.cell_size
      }))
    }
    fn update(accessor, space, object, new_bounds: Option<BoundingBox>) {
      let old_data = match space.get_detector_data (accessor, object) {None => return, Some (a) => a};

      let mut cells = get (accessor, &self.cells);
      let mut new_neighbors =
      let mut old_neighbors =

      if let Some(new_bounds) = new_data.as_ref() {
        for location in new_bounds.locations () {
          if let Some(cell) = cells.get_mut (location) {
            for neighbor in cell.objects {
              old_neighbors.insert (neighbor);
            }
            cell.objects.insert (object.clone());
          }
        }
      }
      if let Some(old_data) = old_data {
        for location in old_bounds.locations () {
          if new_data.map_or(true, |new_bounds| !new_bounds.contains (location)) {
            let cell = cells.entry (location).or_insert (Default::default());
            for neighbor in cell.objects {
              if !new_neighbors.contains (neighbor) {
                old_neighbors.insert (neighbor);
              }
            }
            cell.objects.remove(object.clone());
          }
        }
      }

      for neighbor in old_neighbors {
        if !new_neighbors.contains (neighbor) {
          space.stop_being_neighbors (accessor, [object, neighbor]);
        }
      }
      for neighbor in new_neighbors {
        if !old_neighbors.contains (neighbor) {
          space.become_neighbors (accessor, [object, neighbor]);
        }
      }

      let new_data = new_bounds.map (| new_bounds | DetectorDataPerObject {
        current_grid_bounds: new_bounds.clone(),
        .escapes_bounds_prediction:
      });

      space.set_detector_data (accessor, object, new_data);
      set (accessor, &self.cells, cells);
    }
  }
}

/*
struct NodeBounds<B: BoundingBoxCollisionDetectable> {
  half_size_shift: u32,
  center: [Coordinate; B::DIMENSIONS],
}
fn smallest_containing_node_bounds <B: BoundingBoxCollisionDetectable> (bounds: & BoundingBox<B>)->NodeBounds <B> {
  unimplemented!()
}

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
struct Node<B: BoundingBoxCollisionDetectable> {
  bounds: NodeBounds<B>,
  parent: Option<DataHandle <Node <B>>>,
  larger_cousins: [[Option<DataHandle <Node <B>>>; 2]; B::DIMENSIONS],
  varying: NodeVarying <B>,
}
impl<B: BoundingBoxCollisionDetectable> PersistentlyIdentifiedType for Node<B> {
  const ID: PersistentTypeId = PersistentTypeId(B::ID.0 ^ 0x7c4c8993671ba023);
}
#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
struct NodeVarying<B: BoundingBoxCollisionDetectable> {
  children: [Option<DataHandle <Node <B>>>; 1<<B::DIMENSIONS],
  overlapping_live_nodes: Vec<DataHandle <B::Object>>,
  objects: Vec<DataHandle <B::Object>>,
}

pub struct ObjectData<B: BoundingBoxCollisionDetectable> {
  varying: ObjectVarying<B>,
}
struct ObjectVarying<B: BoundingBoxCollisionDetectable> {
  node: DataHandle <Node <B>>,
}
pub struct BoundingBoxCollisionDetector<B: BoundingBoxCollisionDetectable> {
  root: DataHandle <Node <B>>,
}

impl<B: BoundingBoxCollisionDetectable> BoundingBoxCollisionDetector<B> {
  pub fn insert<A: EventAccessor <Steward = B::Steward>>(accessor: &A, object: &DataHandle<B::Object>, space: &B::Space, location_hint: Option <& DataHandle<B::Object>>) {

  }
  pub fn remove<A: EventAccessor <Steward = B::Steward>>(accessor: &A, object: &DataHandle<B::Object>, space: &B::Space) {

  }
  pub fn neighbors<A: EventAccessor <Steward = B::Steward>>(accessor: &A, object: &DataHandle<B::Object>, space: &B::Space) -> Iter {

  }
}

#[derive (Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct EscapesBounds<B: BoundingBoxCollisionDetectable> {}
impl<B: BoundingBoxCollisionDetectable> PersistentlyIdentifiedType for EscapesBounds<B> {
  const ID: PersistentTypeId = PersistentTypeId(B::ID.0 ^ 0xb0cdbe951b688b70);
}
impl<B: BoundingBoxCollisionDetectable> Event for EscapesBounds<B> {
  type Steward = B::Steward;
  type ExecutionData = ();
  fn execute <Accessor: EventAccessor <Steward = Self::Steward>> (&self, accessor: &mut Accessor) {

  }

  fn undo <Accessor: FutureCleanupAccessor <Steward = Self::Steward>> (&self, accessor: &mut Accessor, _: ()) {
    unimplemented!()
  }
}

impl<B: BoundingBoxCollisionDetectable> BoundingBoxCollisionDetector<B> {
  fn reposition <A: EventAccessor <Steward = B::Steward>> (accessor: &A, object: &DataHandle<B::Object>) {
    let varying = tracking_query (accessor, object.varying);
    let mut current_node = varying.node;
    let new_bounds = B::calculate_current_bounding_box (object);
    let destination_node_bounds = smallest_containing_node_bounds (new_bounds) ;
    let bigger_shift = max (current_node.bounds.half_size_shift, destination_node_bounds.half_size_shift);

    while current_node.bounds.half_size_shift < bigger_shift {
      current_node = current_node.parent.expect("All nodes except the root node have to have a parent, and we should never be trying to navigate to a node bigger than the root");
    }

    let mut destination_ancestor_bounds = destination_node_bounds;
    while destination_ancestor_bounds.half_size_shift < bigger_shift {
      destination_ancestor_bounds = parent_bounds(destination_ancestor_bounds);
    }

    while largest_single_dimension_distance (current_node.bounds, destination_ancestor_bounds) > (1 << destination_ancestor_bounds.half_size_shift) {
      destination_ancestor_bounds = parent_bounds(destination_ancestor_bounds);
      current_node = current_node.parent.expect("All nodes except the root node have to have a parent, and we should never be trying to navigate to a node bigger than the root");
    }

    if current_node.bounds != destination_ancestor_bounds {
      // they should now be the same size and half- or all-overlapping in each dimension.
    }
  }
}

*/
<|endoftext|>


<commit_before>enum my_visitor = @{ mut types: [str] };

impl of intrinsic::ty_visitor for my_visitor {
    fn visit_bot() -> bool {
        self.types += ["bot"];
        #error("visited bot type");
        true
    }
    fn visit_nil() -> bool {
        self.types += ["nil"];
        #error("visited nil type");
        true
    }
    fn visit_bool() -> bool {
        self.types += ["bool"];
        #error("visited bool type");
        true
    }
    fn visit_int() -> bool {
        self.types += ["int"];
        #error("visited int type");
        true
    }
    fn visit_i8() -> bool {
        self.types += ["i8"];
        #error("visited i8 type");
        true
    }
    fn visit_i16() -> bool {
        self.types += ["i16"];
        #error("visited i16 type");
        true
    }
    fn visit_i32() -> bool { true }
    fn visit_i64() -> bool { true }

    fn visit_uint() -> bool { true }
    fn visit_u8() -> bool { true }
    fn visit_u16() -> bool { true }
    fn visit_u32() -> bool { true }
    fn visit_u64() -> bool { true }

    fn visit_float() -> bool { true }
    fn visit_f32() -> bool { true }
    fn visit_f64() -> bool { true }

    fn visit_char() -> bool { true }
    fn visit_str() -> bool { true }

    fn visit_vec_of(_mutbl: uint) -> bool { true }
    fn visit_box_of(_mutbl: uint) -> bool { true }
    fn visit_uniq_of(_mutbl: uint) -> bool { true }
    fn visit_ptr_of(_mutbl: uint) -> bool { true }
    fn visit_rptr_of(_mutbl: uint) -> bool { true }
    fn visit_rec_of(_n_fields: uint) -> bool { true }
    fn visit_rec_field(_name: str/&, _mutbl: uint) -> bool { true }
    fn visit_tup_of(_n_fields: uint) -> bool { true }
    fn visit_tup_field(_mutbl: uint) -> bool { true }
    fn visit_enum_of(_n_variants: uint) -> bool { true }
    fn visit_enum_variant(_name: str/&) -> bool { true }
}

fn main() {
    let v = my_visitor(@{mut types: []});
    let vv = v as intrinsic::ty_visitor;

    intrinsic::visit_ty::<bool>(vv);
    intrinsic::visit_ty::<int>(vv);
    intrinsic::visit_ty::<i8>(vv);
    intrinsic::visit_ty::<i16>(vv);

    for v.types.each {|s|
        io::println(#fmt("type: %s", s));
    }
    assert v.types == ["bool", "int", "i8", "i16"];
}
<commit_msg>Re-xfail reflect-visit-type since it crashes check-fast.<commit_after>// xfail-test
//
// This doesn't work quite yet in check-fast mode. Not sure why. Crashes.

enum my_visitor = @{ mut types: [str] };

impl of intrinsic::ty_visitor for my_visitor {
    fn visit_bot() -> bool {
        self.types += ["bot"];
        #error("visited bot type");
        true
    }
    fn visit_nil() -> bool {
        self.types += ["nil"];
        #error("visited nil type");
        true
    }
    fn visit_bool() -> bool {
        self.types += ["bool"];
        #error("visited bool type");
        true
    }
    fn visit_int() -> bool {
        self.types += ["int"];
        #error("visited int type");
        true
    }
    fn visit_i8() -> bool {
        self.types += ["i8"];
        #error("visited i8 type");
        true
    }
    fn visit_i16() -> bool {
        self.types += ["i16"];
        #error("visited i16 type");
        true
    }
    fn visit_i32() -> bool { true }
    fn visit_i64() -> bool { true }

    fn visit_uint() -> bool { true }
    fn visit_u8() -> bool { true }
    fn visit_u16() -> bool { true }
    fn visit_u32() -> bool { true }
    fn visit_u64() -> bool { true }

    fn visit_float() -> bool { true }
    fn visit_f32() -> bool { true }
    fn visit_f64() -> bool { true }

    fn visit_char() -> bool { true }
    fn visit_str() -> bool { true }

    fn visit_vec_of(_mutbl: uint) -> bool { true }
    fn visit_box_of(_mutbl: uint) -> bool { true }
    fn visit_uniq_of(_mutbl: uint) -> bool { true }
    fn visit_ptr_of(_mutbl: uint) -> bool { true }
    fn visit_rptr_of(_mutbl: uint) -> bool { true }
    fn visit_rec_of(_n_fields: uint) -> bool { true }
    fn visit_rec_field(_name: str/&, _mutbl: uint) -> bool { true }
    fn visit_tup_of(_n_fields: uint) -> bool { true }
    fn visit_tup_field(_mutbl: uint) -> bool { true }
    fn visit_enum_of(_n_variants: uint) -> bool { true }
    fn visit_enum_variant(_name: str/&) -> bool { true }
}

fn main() {
    let v = my_visitor(@{mut types: []});
    let vv = v as intrinsic::ty_visitor;

    intrinsic::visit_ty::<bool>(vv);
    intrinsic::visit_ty::<int>(vv);
    intrinsic::visit_ty::<i8>(vv);
    intrinsic::visit_ty::<i16>(vv);

    for v.types.each {|s|
        io::println(#fmt("type: %s", s));
    }
    assert v.types == ["bool", "int", "i8", "i16"];
}
<|endoftext|>


<commit_before>use eyre::{bail, Result};
use schemars::JsonSchema;
use serde::Serialize;
use serde_with::skip_serializing_none;
use std::{
    collections::BTreeMap,
    path::{Path, PathBuf},
    sync::{
        mpsc::{channel, TryRecvError},
        Arc, Mutex, MutexGuard,
    },
    time::UNIX_EPOCH,
};

use crate::pubsub::{self, ProjectFileEvent};

/// # A file or directory within a `Project`
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct File {
    /// The absolute path of the file or directory
    pub path: PathBuf,

    /// The name of the file or directory
    pub name: String,

    /// Time that the file was last modified (Unix Epoch timestamp)
    pub modified: Option<u64>,

    /// Size of the file in bytes
    pub size: Option<u64>,

    /// Format of the file
    ///
    /// Usually this is the lower cased filename extension (if any)
    /// but may also be normalized. May be more convenient,
    /// and usually more available, than the `media_type` property.
    pub format: Option<String>,

    /// The media type (aka MIME type) of the file
    pub media_type: Option<String>,

    /// The SHA1 hash of the contents of the file
    pub sha1: Option<String>,

    /// The parent `File`, if any
    pub parent: Option<PathBuf>,

    /// If a directory, a list of the canonical paths of the files within it.
    /// Otherwise, `None`.
    pub children: Option<Vec<PathBuf>>,
}

impl File {
    pub fn load(path: &Path) -> Result<(PathBuf, File)> {
        let path = path.canonicalize()?;

        let name = path
            .file_name()
            .map(|os_str| os_str.to_string_lossy())
            .unwrap_or_default()
            .into();

        let (modified, size) = match path.metadata() {
            Ok(metadata) => {
                #[allow(clippy::bind_instead_of_map)]
                let modified = metadata
                    .modified()
                    .ok()
                    .and_then(|time| time.duration_since(UNIX_EPOCH).ok())
                    .and_then(|duration| Some(duration.as_secs()));
                let size = Some(metadata.len());
                (modified, size)
            }
            Err(_) => (None, None),
        };

        let format = path
            .extension()
            .map(|ext| ext.to_string_lossy().to_lowercase());

        let (media_type, children) = if path.is_file() {
            let media_type = if let Some(ext) = &format {
                mime_guess::from_ext(&ext)
                    .first()
                    .map(|mime| mime.essence_str().to_string())
                    .or_else(|| match ext.as_str() {
                        // Add MIME types that are not registered
                        // See https://github.com/jupyter/jupyter/issues/68
                        "ipynb" => Some("application/ipynb+json".to_string()),
                        _ => None,
                    })
            } else {
                None
            };

            (media_type, None)
        } else {
            (None, Some(Vec::new()))
        };

        let file = File {
            path,
            name,
            modified,
            size,
            format,
            media_type,
            children,
            ..Default::default()
        };

        Ok((file.path.clone(), file))
    }
}

/// # The set of `File`s within a `Project`
#[derive(Debug, Default, Clone, JsonSchema, Serialize)]
pub struct Files {
    /// A mutual exclusion lock used by a watcher thread
    /// when updating this file set
    #[serde(flatten)]
    pub registry: FileRegistry,

    #[serde(skip)]
    pub watcher: Option<std::sync::mpsc::Sender<()>>,
}

type FileRegistry = Arc<Mutex<BTreeMap<PathBuf, File>>>;

impl Files {
    /// Load files from a folder
    pub fn load(folder: &str, watch: bool) -> Result<Files> {
        let path = Path::new(folder).canonicalize()?;

        // Collect all the files
        let mut files = walkdir::WalkDir::new(&path)
            .into_iter()
            .filter_map(|entry| {
                let entry = match entry.ok() {
                    Some(entry) => entry,
                    None => return None,
                };
                File::load(entry.path()).ok()
            })
            .into_iter()
            .collect::<BTreeMap<PathBuf, File>>();

        // Resolve `parent` and `children` properties
        // This needs to clone the files to avoid mutable borrow twice,
        // there may be a more efficient alternative
        let mut children: BTreeMap<PathBuf, Vec<PathBuf>> = BTreeMap::new();
        for (path, file) in &mut files {
            if let Some(parent_path) = path.parent() {
                let parent_path = parent_path.to_path_buf();
                file.parent = Some(parent_path.clone());
                children
                    .entry(parent_path)
                    .or_insert_with(Vec::new)
                    .push(path.clone());
            }
        }
        for (path, vec) in children {
            if let Some(parent) = files.get_mut(&path) {
                parent.children = Some(vec)
            }
        }

        // Create a registry of the files
        let registry = Arc::new(Mutex::new(files));

        // Watch files and make updates as needed
        let watcher = if watch {
            let project = path.clone();
            let registry = Arc::clone(&registry);
            let (thread_sender, thread_receiver) = channel();
            std::thread::spawn(move || -> Result<()> {
                use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
                use std::time::Duration;

                let (watcher_sender, watcher_receiver) = channel();
                let mut watcher = watcher(watcher_sender, Duration::from_secs(1))?;
                watcher.watch(&path, RecursiveMode::Recursive).unwrap();

                let handle_event = |event| match event {
                    DebouncedEvent::Create(path) => Files::created(&project, &registry, &path),
                    DebouncedEvent::Remove(path) => Files::removed(&project, &registry, &path),
                    DebouncedEvent::Rename(from, to) => {
                        Files::renamed(&project, &registry, &from, &to)
                    }
                    DebouncedEvent::Write(path) => Files::modified(&project, &registry, &path),
                    _ => {}
                };

                let project = path.display().to_string();
                let span = tracing::info_span!("file_watch", project = project.as_str());
                let _enter = span.enter();
                tracing::debug!("Starting project file watch: {}", project);

                loop {
                    if let Err(TryRecvError::Disconnected) = thread_receiver.try_recv() {
                        tracing::debug!("Ending project file watch: {}", project);
                        break;
                    }
                    match watcher_receiver.recv() {
                        Ok(event) => handle_event(event),
                        Err(error) => tracing::error!("Watch error: {:?}", error),
                    }
                }

                Ok(())
            });

            Some(thread_sender)
        } else {
            None
        };

        let files = Files { registry, watcher };
        Ok(files)
    }

    pub fn obtain(&self) -> Result<MutexGuard<BTreeMap<PathBuf, File>>> {
        match self.registry.try_lock() {
            Ok(registry) => Ok(registry),
            Err(error) => bail!("Unable to get file registry: {}", error),
        }
    }

    pub fn created(project: &Path, registry: &FileRegistry, path: &Path) {
        let mut registry = registry.lock().unwrap();

        let file = if let Ok((path, file)) = File::load(path) {
            registry.insert(path, file.clone());
            // TODO: resolve parent/child
            Some(file)
        } else {
            None
        };

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: path.into(),
            kind: "created".into(),
            file,
            files: Some(registry.clone()),
        })
    }

    pub fn removed(project: &Path, registry: &FileRegistry, path: &Path) {
        let mut registry = registry.lock().unwrap();

        registry.remove(path);
        // TODO: Update parents

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: path.into(),
            kind: "removed".into(),
            file: None,
            files: Some(registry.clone()),
        })
    }

    pub fn renamed(project: &Path, registry: &FileRegistry, path: &Path, _to: &Path) {
        let registry = registry.lock().unwrap();

        // TODO

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: path.into(),
            kind: "renamed".into(),
            file: None,
            files: Some(registry.clone()),
        })
    }

    pub fn modified(project: &Path, registry: &FileRegistry, path: &Path) {
        let mut registry = registry.lock().unwrap();

        let file = if let Ok((path, file)) = File::load(path) {
            registry.insert(path, file.clone());
            Some(file)
        } else {
            None
        };

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: path.into(),
            kind: "modified".into(),
            file,
            files: Some(registry.clone()),
        })
    }
}
<commit_msg>feat(Files): Mirror file system changes in memory<commit_after>use eyre::{bail, Result};
use schemars::JsonSchema;
use serde::Serialize;
use serde_with::skip_serializing_none;
use std::{
    collections::{btree_map::Entry, BTreeMap, BTreeSet},
    path::{Path, PathBuf},
    sync::{
        mpsc::{channel, TryRecvError},
        Arc, Mutex, MutexGuard,
    },
    time::UNIX_EPOCH,
};

use crate::pubsub::{self, ProjectFileEvent};

/// # A file or directory within a `Project`
#[skip_serializing_none]
#[derive(Debug, Default, Clone, JsonSchema, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct File {
    /// The absolute path of the file or directory
    pub path: PathBuf,

    /// The name of the file or directory
    pub name: String,

    /// Time that the file was last modified (Unix Epoch timestamp)
    pub modified: Option<u64>,

    /// Size of the file in bytes
    pub size: Option<u64>,

    /// Format of the file
    ///
    /// Usually this is the lower cased filename extension (if any)
    /// but may also be normalized. May be more convenient,
    /// and usually more available, than the `media_type` property.
    pub format: Option<String>,

    /// The media type (aka MIME type) of the file
    pub media_type: Option<String>,

    /// The SHA1 hash of the contents of the file
    pub sha1: Option<String>,

    /// The parent `File`, if any
    pub parent: Option<PathBuf>,

    /// If a directory, a list of the canonical paths of the files within it.
    /// Otherwise, `None`.
    ///
    /// A `BTreeSet` rather than a `Vec` so that paths are ordered without
    /// having to be resorted after insertions. Another option is `BinaryHeap`
    /// but `BinaryHeap::retain` is  only on nightly and so is awkward to use.
    pub children: Option<BTreeSet<PathBuf>>,
}

impl File {
    pub fn load(path: &Path) -> Result<(PathBuf, File)> {
        let path = path.canonicalize()?;

        let name = path
            .file_name()
            .map(|os_str| os_str.to_string_lossy())
            .unwrap_or_default()
            .into();

        let (modified, size) = match path.metadata() {
            Ok(metadata) => {
                #[allow(clippy::bind_instead_of_map)]
                let modified = metadata
                    .modified()
                    .ok()
                    .and_then(|time| time.duration_since(UNIX_EPOCH).ok())
                    .and_then(|duration| Some(duration.as_secs()));
                let size = Some(metadata.len());
                (modified, size)
            }
            Err(_) => (None, None),
        };

        let format = path
            .extension()
            .map(|ext| ext.to_string_lossy().to_lowercase());

        let (media_type, children) = if path.is_file() {
            let media_type = if let Some(ext) = &format {
                mime_guess::from_ext(&ext)
                    .first()
                    .map(|mime| mime.essence_str().to_string())
                    .or_else(|| match ext.as_str() {
                        // Add MIME types that are not registered
                        // See https://github.com/jupyter/jupyter/issues/68
                        "ipynb" => Some("application/ipynb+json".to_string()),
                        _ => None,
                    })
            } else {
                None
            };

            (media_type, None)
        } else {
            (None, Some(BTreeSet::new()))
        };

        let file = File {
            path,
            name,
            modified,
            size,
            format,
            media_type,
            children,
            ..Default::default()
        };

        Ok((file.path.clone(), file))
    }
}

/// # The set of `File`s within a `Project`
#[derive(Debug, Default, Clone, JsonSchema, Serialize)]
pub struct Files {
    /// A mutual exclusion lock used by a watcher thread
    /// when updating this file set
    #[serde(flatten)]
    pub registry: FileRegistry,

    #[serde(skip)]
    pub watcher: Option<std::sync::mpsc::Sender<()>>,
}

type FileRegistry = Arc<Mutex<BTreeMap<PathBuf, File>>>;

impl Files {
    /// Load files from a folder
    pub fn load(folder: &str, watch: bool) -> Result<Files> {
        let path = Path::new(folder).canonicalize()?;

        // Collect all the files
        let mut files = walkdir::WalkDir::new(&path)
            .into_iter()
            .filter_map(|entry| {
                let entry = match entry.ok() {
                    Some(entry) => entry,
                    None => return None,
                };
                File::load(entry.path()).ok()
            })
            .into_iter()
            .collect::<BTreeMap<PathBuf, File>>();

        // Resolve `parent` and `children` properties
        // This needs to clone the files to avoid mutable borrow twice,
        // there may be a more efficient alternative
        let mut children: BTreeMap<PathBuf, BTreeSet<PathBuf>> = BTreeMap::new();
        for (path, file) in &mut files {
            if let Some(parent_path) = path.parent() {
                let parent_path = parent_path.to_path_buf();
                file.parent = Some(parent_path.clone());
                children
                    .entry(parent_path)
                    .or_insert_with(BTreeSet::new)
                    .insert(path.clone());
            }
        }
        for (path, vec) in children {
            if let Some(parent) = files.get_mut(&path) {
                parent.children = Some(vec)
            }
        }

        // Create a registry of the files
        let registry = Arc::new(Mutex::new(files));

        // Watch files and make updates as needed
        let watcher = if watch {
            let project = path.clone();
            let registry = Arc::clone(&registry);
            let (thread_sender, thread_receiver) = channel();
            std::thread::spawn(move || -> Result<()> {
                use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
                use std::time::Duration;

                let (watcher_sender, watcher_receiver) = channel();
                let mut watcher = watcher(watcher_sender, Duration::from_secs(1))?;
                watcher.watch(&path, RecursiveMode::Recursive).unwrap();

                let handle_event = |event| match event {
                    DebouncedEvent::Create(path) => Files::created(&project, &registry, &path),
                    DebouncedEvent::Remove(path) => Files::removed(&project, &registry, &path),
                    DebouncedEvent::Rename(from, to) => {
                        Files::renamed(&project, &registry, &from, &to)
                    }
                    DebouncedEvent::Write(path) => Files::modified(&project, &registry, &path),
                    _ => {}
                };

                let project = path.display().to_string();
                let span = tracing::info_span!("file_watch", project = project.as_str());
                let _enter = span.enter();
                tracing::debug!("Starting project file watch: {}", project);

                loop {
                    if let Err(TryRecvError::Disconnected) = thread_receiver.try_recv() {
                        tracing::debug!("Ending project file watch: {}", project);
                        break;
                    }
                    match watcher_receiver.recv() {
                        Ok(event) => handle_event(event),
                        Err(error) => tracing::error!("Watch error: {:?}", error),
                    }
                }

                Ok(())
            });

            Some(thread_sender)
        } else {
            None
        };

        let files = Files { registry, watcher };
        Ok(files)
    }

    // Obtain the file registry
    pub fn obtain(&self) -> Result<MutexGuard<BTreeMap<PathBuf, File>>> {
        match self.registry.try_lock() {
            Ok(registry) => Ok(registry),
            Err(error) => bail!("Unable to get file registry: {}", error),
        }
    }

    // Update a project file registry when a file is created
    pub fn created(project: &Path, registry: &FileRegistry, path: &Path) {
        let mut registry = registry.lock().unwrap();

        // Load the file, insert it and add it to it's parent's children
        let file = if let Ok((path, file)) = File::load(path) {
            registry.insert(path.clone(), file.clone());
            if let Some(parent) = path.parent().and_then(|parent| registry.get_mut(parent)) {
                if let Some(children) = &mut parent.children {
                    children.insert(path);
                }
            }

            Some(file)
        } else {
            None
        };

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: path.into(),
            kind: "created".into(),
            file,
            files: Some(registry.clone()),
        })
    }

    // Update a project file registry when a file is removed
    pub fn removed(project: &Path, registry: &FileRegistry, path: &Path) {
        let mut registry = registry.lock().unwrap();

        // Remove the file and remove it from its parent's children
        registry.remove(path);
        if let Some(parent) = path.parent().and_then(|parent| registry.get_mut(parent)) {
            if let Some(children) = &mut parent.children {
                children.remove(path);
            }
        }

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: path.into(),
            kind: "removed".into(),
            file: None,
            files: Some(registry.clone()),
        })
    }

    // Update a project file registry when a file is renamed
    pub fn renamed(project: &Path, registry: &FileRegistry, old_path: &Path, new_path: &Path) {
        let mut registry = registry.lock().unwrap();

        // Recursively rename children of a file
        fn rename_children(
            registry: &mut BTreeMap<PathBuf, File>,
            file: &mut File,
            old_path: &Path,
            new_path: &Path,
        ) {
            if let Some(children) = &mut file.children {
                let mut new_children = BTreeSet::new();
                for child_old_path in children.iter() {
                    let child_new_path = new_path.join(
                        child_old_path
                            .strip_prefix(old_path)
                            .expect("Unable to strip old path"),
                    );

                    if let Entry::Occupied(entry) = registry.entry(child_old_path.into()) {
                        let mut file = entry.remove();
                        file.path = child_new_path.clone();
                        file.parent = child_new_path.parent().map(|parent| parent.into());
                        rename_children(registry, &mut file, child_old_path, &child_new_path);
                        registry.insert(child_new_path.clone(), file);
                    }

                    new_children.insert(child_new_path);
                }
                file.children = Some(new_children);
            }
        }

        // Move the file
        let file = match registry.entry(old_path.into()) {
            Entry::Occupied(entry) => {
                // Update it's path and parent properties, and the paths of
                // all it's descendants. Move the file from old to new path.
                let mut file = entry.remove();
                file.path = new_path.into();
                file.parent = new_path.parent().map(|parent| parent.into());
                rename_children(&mut *registry, &mut file, old_path, new_path);
                registry.insert(new_path.into(), file.clone());
                Some(file)
            }
            Entry::Vacant(_) => {
                // The entry should not be empty, but in case it is, load the file from,
                // and insert it at, the new path
                if let Ok((_path, file)) = File::load(new_path) {
                    registry.insert(new_path.into(), file.clone());
                    Some(file)
                } else {
                    None
                }
            }
        };

        // Remove the old path from the old parent's children
        if let Some(parent) = old_path
            .parent()
            .and_then(|parent| registry.get_mut(parent))
        {
            if let Some(children) = &mut parent.children {
                children.remove(old_path);
            }
        }

        // Insert the new path to the new parent's children
        if let Some(parent) = new_path
            .parent()
            .and_then(|parent| registry.get_mut(parent))
        {
            if let Some(children) = &mut parent.children {
                children.insert(new_path.into());
            }
        }

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: old_path.into(),
            kind: "renamed".into(),
            file,
            files: Some(registry.clone()),
        })
    }

    // Update a project file registry when a file is modified
    pub fn modified(project: &Path, registry: &FileRegistry, path: &Path) {
        let mut registry = registry.lock().unwrap();

        // Insert the file
        let file = if let Ok((path, file)) = File::load(path) {
            registry.insert(path, file.clone());
            Some(file)
        } else {
            None
        };

        pubsub::publish_project_file(ProjectFileEvent {
            project: project.into(),
            path: path.into(),
            kind: "modified".into(),
            file,
            files: Some(registry.clone()),
        })
    }
}
<|endoftext|>