<commit_before>#![feature(test)]

#![allow(dead_code)]

extern crate test;

extern crate polydraw;

use std::cmp::{min, max};
use std::i64;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::RGB;
use polydraw::geom::point::Point;
use polydraw::geom::triangle::Triangle;
use polydraw::geom::ring::Ring;
use polydraw::geom::clip::{h_split_edge, v_split_edge, hv_split};

const DIV_PER_PIXEL: i64 = 1000;
const HALF_DIV_PER_PIXEL: i64 = DIV_PER_PIXEL / 2;
const DOUBLE_PIXEL_AREA: i64 = DIV_PER_PIXEL * DIV_PER_PIXEL * 2;

#[inline]
fn to_px(v: i64) -> i64 {
   v / DIV_PER_PIXEL
}

#[inline]
fn from_px(v: i64) -> i64 {
   v as i64 * DIV_PER_PIXEL
}

struct TriangleRenderer {
   tr: Triangle,
   colors: Vec<RGB>,
   up: Ring<Point>,
   right: Ring<Point>,
   left: Ring<Point>,
}

impl TriangleRenderer {
   fn new() -> Self {
      let tr = Triangle::new(
         Point::new(from_px(100), from_px(450)),
         Point::new(from_px(600), from_px(600)),
         Point::new(from_px(350), from_px(100)),
      );

      let mut colors = Vec::with_capacity(256);
      for i in 0..256_usize {
         colors.push(
            RGB::new(
               (i * 127 / 255) as u8, (i * 223 / 255) as u8, i as u8
            )
         );
      }

      TriangleRenderer {
         tr: tr,
         colors: colors,
         up: Ring::new(131072),
         right: Ring::new(524288),
         left: Ring::new(524288),
      }
   }

   fn solid_follow(&mut self, x_split: i64, y_world: i64, y_split: i64) -> Option<i64> {
      let right_start = self.right.start();
      let right_end = self.right.end();

      if right_end - right_start > 3 {
         let top_left = Point::new(x_split, y_split);
         for top_left_i in right_start..right_end {
            if self.right[top_left_i] == top_left {

               let bottom_left = Point::new(x_split, y_world);
               let bottom_left_i = self.right.next_index(top_left_i);
               if self.right[bottom_left_i] == bottom_left {

                  let bottom_right_i = self.right.next_index(bottom_left_i);
                  if self.right[bottom_right_i].y == y_world {

                     let top_right_i = self.right.prev_index(top_left_i);
                     if self.right[top_right_i].y == y_split {
                        let delta_min = min(
                           self.right[top_right_i].x,
                           self.right[bottom_right_i].x
                        ) - x_split;

                        if delta_min >= DIV_PER_PIXEL {
                           return Some(to_px(delta_min));
                        }
                     }
                  }
               }
            }
         }
      }

      None
   }
}

impl Renderer for TriangleRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      self.tr.a.x += DIV_PER_PIXEL;
      if self.tr.a.x >= from_px(frame.width as i64) {
         self.tr.a.x = 0;
      }

      if self.tr.clockwise() {
         self.tr.change_orientation();
      }

      let a = self.tr.a;
      let b = self.tr.b;
      let c = self.tr.c;

      let min_y = max(to_px(min3(a.y, b.y, c.y)), 0);
      let max_y = min(to_px(max3(a.y, b.y, c.y)), frame.height as i64 - 1);

      self.up.push(a); self.up.push(b); self.up.push(c);

      for y in min_y..max_y+1 {
         let y_world = from_px(y);
         let y_split = y_world + DIV_PER_PIXEL;

         hv_split(h_split_edge, y_split, &mut self.right, &mut self.up);

         let (min_x, max_x) = min_max_x(&self.right);

         let mut x = min_x;

         while x < max_x {
            let x_world = from_px(x);
            let x_split = x_world + DIV_PER_PIXEL;

            hv_split(v_split_edge, x_split, &mut self.left, &mut self.right);

            plot_poly_pixel(frame, x, y, &self.left, &self.colors);

            self.left.consume();

            x += 1;

            match self.solid_follow(x_split, y_world, y_split) {
               Some(delta) => {
                  let x_split_right = x_split + from_px(delta);

                  hv_split(v_split_edge, x_split_right, &mut self.left, &mut self.right);

                  for solid_x in x..x + delta {
                     frame.put_pixel(solid_x as i32, y as i32, &self.colors[255]);
                  }

                  self.left.consume();

                  x += delta;
               },
               None => {}
            }
         }

         plot_poly_pixel(frame, max_x, y, &self.right, &self.colors);

         self.right.consume();
      }
   }
}

#[inline]
fn print_points(name: &str, points: &Ring<Point>) {
   print!("{}: ", name);

   print!("s {} e {} ", points.start(), points.end());

   for p in points[..].iter() {
      print!("({:?}, {:?}) ", p.x, p.y);
   }

   println!("");
}

#[inline]
fn plot_poly_pixel(frame: &mut Frame, x: i64, y: i64, points: &Ring<Point>, colors: &Vec<RGB>) {
   let area = double_area(points);

   assert!(area >= 0);

   frame.put_pixel(x as i32, y as i32, &colors[(255 * area / DOUBLE_PIXEL_AREA) as usize]);
}

#[inline]
fn min_max_x(points: &Ring<Point>) -> (i64, i64) {
   let mut min_x = i64::MAX;
   let mut max_x = i64::MIN;
   for p in points[..].iter() {
      if p.x > max_x {
         max_x = p.x;
      }

      if p.x < min_x {
         min_x = p.x;
      }
   }

   (to_px(min_x), to_px(max_x))
}

#[inline]
pub fn min3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   min(min(v1, v2), v3)
}

#[inline]
pub fn max3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   max(max(v1, v2), v3)
}

#[inline]
pub fn double_area(points: &Ring<Point>) -> i64 {
   if points.len() <= 2 {
      return 0;
   }

   let mut p1 = points.last().unwrap();

   let mut area = 0;

   for p2 in points[..].iter() {
      area += p1.x * p2.y - p1.y * p2.x;

      p1 = p2;
   }

   area
}

fn main() {
   let mut renderer = TriangleRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Triangle")
      .run();
}
<commit_msg>Dead code cleanup in the triangle example<commit_after>#![feature(test)]

extern crate test;

extern crate polydraw;

use std::cmp::{min, max};
use std::i64;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::RGB;
use polydraw::geom::point::Point;
use polydraw::geom::triangle::Triangle;
use polydraw::geom::ring::Ring;
use polydraw::geom::clip::{h_split_edge, v_split_edge, hv_split};

const DIV_PER_PIXEL: i64 = 1000;
const DOUBLE_PIXEL_AREA: i64 = DIV_PER_PIXEL * DIV_PER_PIXEL * 2;

#[inline]
fn to_px(v: i64) -> i64 {
   v / DIV_PER_PIXEL
}

#[inline]
fn from_px(v: i64) -> i64 {
   v as i64 * DIV_PER_PIXEL
}

struct TriangleRenderer {
   tr: Triangle,
   colors: Vec<RGB>,
   up: Ring<Point>,
   right: Ring<Point>,
   left: Ring<Point>,
}

impl TriangleRenderer {
   fn new() -> Self {
      let tr = Triangle::new(
         Point::new(from_px(100), from_px(450)),
         Point::new(from_px(600), from_px(600)),
         Point::new(from_px(350), from_px(100)),
      );

      let mut colors = Vec::with_capacity(256);
      for i in 0..256_usize {
         colors.push(
            RGB::new(
               (i * 127 / 255) as u8, (i * 223 / 255) as u8, i as u8
            )
         );
      }

      TriangleRenderer {
         tr: tr,
         colors: colors,
         up: Ring::new(131072),
         right: Ring::new(524288),
         left: Ring::new(524288),
      }
   }

   fn solid_follow(&mut self, x_split: i64, y_world: i64, y_split: i64) -> Option<i64> {
      let right_start = self.right.start();
      let right_end = self.right.end();

      if right_end - right_start > 3 {
         let top_left = Point::new(x_split, y_split);
         for top_left_i in right_start..right_end {
            if self.right[top_left_i] == top_left {

               let bottom_left = Point::new(x_split, y_world);
               let bottom_left_i = self.right.next_index(top_left_i);
               if self.right[bottom_left_i] == bottom_left {

                  let bottom_right_i = self.right.next_index(bottom_left_i);
                  if self.right[bottom_right_i].y == y_world {

                     let top_right_i = self.right.prev_index(top_left_i);
                     if self.right[top_right_i].y == y_split {
                        let delta_min = min(
                           self.right[top_right_i].x,
                           self.right[bottom_right_i].x
                        ) - x_split;

                        if delta_min >= DIV_PER_PIXEL {
                           return Some(to_px(delta_min));
                        }
                     }
                  }
               }
            }
         }
      }

      None
   }
}

impl Renderer for TriangleRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      self.tr.a.x += DIV_PER_PIXEL;
      if self.tr.a.x >= from_px(frame.width as i64) {
         self.tr.a.x = 0;
      }

      if self.tr.clockwise() {
         self.tr.change_orientation();
      }

      let a = self.tr.a;
      let b = self.tr.b;
      let c = self.tr.c;

      let min_y = max(to_px(min3(a.y, b.y, c.y)), 0);
      let max_y = min(to_px(max3(a.y, b.y, c.y)), frame.height as i64 - 1);

      self.up.push(a); self.up.push(b); self.up.push(c);

      for y in min_y..max_y+1 {
         let y_world = from_px(y);
         let y_split = y_world + DIV_PER_PIXEL;

         hv_split(h_split_edge, y_split, &mut self.right, &mut self.up);

         let (min_x, max_x) = min_max_x(&self.right);

         let mut x = min_x;

         while x < max_x {
            let x_world = from_px(x);
            let x_split = x_world + DIV_PER_PIXEL;

            hv_split(v_split_edge, x_split, &mut self.left, &mut self.right);

            plot_poly_pixel(frame, x, y, &self.left, &self.colors);

            self.left.consume();

            x += 1;

            match self.solid_follow(x_split, y_world, y_split) {
               Some(delta) => {
                  let x_split_right = x_split + from_px(delta);

                  hv_split(v_split_edge, x_split_right, &mut self.left, &mut self.right);

                  for solid_x in x..x + delta {
                     frame.put_pixel(solid_x as i32, y as i32, &self.colors[255]);
                  }

                  self.left.consume();

                  x += delta;
               },
               None => {}
            }
         }

         plot_poly_pixel(frame, max_x, y, &self.right, &self.colors);

         self.right.consume();
      }
   }
}

#[allow(dead_code)]
#[inline]
fn print_points(name: &str, points: &Ring<Point>) {
   print!("{}: ", name);

   print!("s {} e {} ", points.start(), points.end());

   for p in points[..].iter() {
      print!("({:?}, {:?}) ", p.x, p.y);
   }

   println!("");
}

#[inline]
fn plot_poly_pixel(frame: &mut Frame, x: i64, y: i64, points: &Ring<Point>, colors: &Vec<RGB>) {
   let area = double_area(points);

   assert!(area >= 0);

   frame.put_pixel(x as i32, y as i32, &colors[(255 * area / DOUBLE_PIXEL_AREA) as usize]);
}

#[inline]
fn min_max_x(points: &Ring<Point>) -> (i64, i64) {
   let mut min_x = i64::MAX;
   let mut max_x = i64::MIN;
   for p in points[..].iter() {
      if p.x > max_x {
         max_x = p.x;
      }

      if p.x < min_x {
         min_x = p.x;
      }
   }

   (to_px(min_x), to_px(max_x))
}

#[inline]
pub fn min3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   min(min(v1, v2), v3)
}

#[inline]
pub fn max3<T: Ord>(v1: T, v2: T, v3: T) -> T {
   max(max(v1, v2), v3)
}

#[inline]
pub fn double_area(points: &Ring<Point>) -> i64 {
   if points.len() <= 2 {
      return 0;
   }

   let mut p1 = points.last().unwrap();

   let mut area = 0;

   for p2 in points[..].iter() {
      area += p1.x * p2.y - p1.y * p2.x;

      p1 = p2;
   }

   area
}

fn main() {
   let mut renderer = TriangleRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Triangle")
      .run();
}
<|endoftext|>


<commit_before>
extern crate cratesfyi;
extern crate clap;
#[macro_use]
extern crate log;
extern crate time;

use std::env;
use std::fs;
use std::process::{Command, exit};
use std::path::PathBuf;

use cratesfyi::docbuilder::{DocBuilder, DocBuilderError, command_result};
use cratesfyi::docbuilder::crte::Crate;
use clap::{Arg, App, SubCommand};
use log::{LogLevel, LogLevelFilter, LogRecord, LogMetadata};
use time::now;

// I am using really simple Logger in this program
struct Logger;

impl log::Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} {} - {}", now().to_utc().rfc3339(), record.level(), record.args());
        }
    }
}

fn update_crates_io_index(path: &PathBuf) -> Result<String, String> {
    info!("Updating crates.io-index");

    if path.exists() {
        let cwd = env::current_dir().unwrap();
        env::set_current_dir(path).unwrap();
        let res = command_result(Command::new("git").arg("pull").output().unwrap());
        env::set_current_dir(cwd).unwrap();
        res
    } else {
        command_result(Command::new("git")
            .arg("clone")
            .arg("https://github.com/rust-lang/crates.io-index.git")
            .arg(path.to_str().unwrap())
            .output().unwrap())
    }
}

// This will remove everything in CWD!!!
fn clean_build_dir() -> Result<(), DocBuilderError> {

    for file in try!(env::current_dir().unwrap()
                     .read_dir().map_err(DocBuilderError::RemoveBuildDir)) {
        let file = try!(file.map_err(DocBuilderError::RemoveBuildDir));
        let path = file.path();

        if path.file_name().unwrap() == ".cargo" ||
            path.file_name().unwrap() == ".crates.io-index" {
                continue;
            }

        if path.is_dir() {
            try!(fs::remove_dir_all(path).map_err(DocBuilderError::RemoveBuildDir));
        } else {
            try!(fs::remove_file(path).map_err(DocBuilderError::RemoveBuildDir));
        }
    }

    Ok(())
}

fn main() {
    log::set_logger(|max_log_level| {
        max_log_level.set(LogLevelFilter::Info);
        Box::new(Logger)
    }).unwrap();

    let matches = App::new("cratesfyi")
                      .version(env!("CARGO_PKG_VERSION"))
                      .about("Crates for your info!")
                      .subcommand(SubCommand::with_name("build")
                                      .about("Builds documentation in a chroot environment")
                                      .arg(Arg::with_name("PREFIX")
                                               .short("P")
                                               .long("prefix")
                                               .takes_value(true))
                                      .arg(Arg::with_name("DESTINATION")
                                               .short("d")
                                               .long("destination")
                                               .help("Sets destination path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_PATH")
                                               .short("c")
                                               .long("chroot")
                                               .help("Sets chroot path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_USER")
                                               .short("u")
                                               .long("chroot-user")
                                               .help("Sets chroot user name")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CRATES_IO_INDEX_PATH")
                                               .long("crates-io-index-path")
                                               .help("Sets crates.io-index path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("LOGS_PATH")
                                               .long("logs-path")
                                               .help("Sets logs path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("SKIP_IF_EXISTS")
                                               .short("s")
                                               .long("skip")
                                               .help("Skips building documentation if \
                                                      documentation exists"))
                                      .arg(Arg::with_name("SKIP_IF_LOG_EXISTS")
                                               .long("skip-if-log-exists")
                                               .help("Skips building documentation if build \
                                                      log exists"))
                                      .arg(Arg::with_name("KEEP_BUILD_DIRECTORY")
                                               .short("-k")
                                               .long("keep-build-directory")
                                               .help("Keeps build directory after build."))
                                      .subcommand(SubCommand::with_name("download-sources")
                                                      .about("Downloads sources of all crates"))
                                      .subcommand(SubCommand::with_name("world")
                                                      .about("Builds documentation of every \
                                                              crate")
                                                      .arg(Arg::with_name("SKIP_OLDEST_VERSIONS")
                                                               .long("skip-oldest-versions")
                                                               .help("Skips trying to build \
                                                                      oldest versions \
                                                                      if build fails"))
                                                      .arg(Arg::with_name("BUILD_ONLY_LATEST_V\
                                                                           ERSION")
                                                               .long("build-only-latest-versio\
                                                                      n")
                                                               .help("Builds only latest \
                                                                      version of crate and \
                                                                      skips oldest versions")))
                                      .subcommand(SubCommand::with_name("crate")
                                                      .about("Builds documentation for a crate")
                                                      .arg(Arg::with_name("CRATE_NAME")
                                                               .index(1)
                                                               .required(true)
                                                               .help("Crate name"))
                                                      .arg(Arg::with_name("CRATE_VERSION")
                                                               .index(2)
                                                               .required(true)
                                                               .help("Version of crate"))))
                      .subcommand(SubCommand::with_name("build-doc")
                                      .about("Builds documentation in CWD")
                                      .arg(Arg::with_name("CRATES_IO_INDEX_PATH")
                                               .short("p")
                                               .long("crates-io-index-path")
                                               .help("Sets crates.io-index path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CLEAN")
                                               .short("c")
                                               .long("clean")
                                               .help("Clean build dir before building"))
                                      .arg(Arg::with_name("CRATE_NAME")
                                               .index(1)
                                               .required(true)
                                               .help("Crate name"))
                                      .arg(Arg::with_name("CRATE_VERSION")
                                               .index(2)
                                               .required(true)
                                               .help("Version of crate")))
                      .get_matches();

    // DocBuilder
    if let Some(matches) = matches.subcommand_matches("build") {
        let mut dbuilder = {
            if let Some(prefix) = matches.value_of("PREFIX") {
                DocBuilder::from_prefix(PathBuf::from(prefix))
            } else {
                DocBuilder::default()
            }
        };

        // set destination
        if let Some(destination) = matches.value_of("DESTINATION") {
            dbuilder.destination(PathBuf::from(destination));
        }

        // set chroot path
        if let Some(chroot_path) = matches.value_of("CHROOT_PATH") {
            dbuilder.destination(PathBuf::from(chroot_path));
        }

        // set chroot user name
        if let Some(chroot_user) = matches.value_of("CHROOT_USER") {
            dbuilder.chroot_user(chroot_user.to_string());
        }

        // set crates.io-index path
        if let Some(crates_io_index_path) = matches.value_of("CRATES_IO_INDEX_PATH") {
            dbuilder.crates_io_index_path(PathBuf::from(crates_io_index_path));
        }

        // set logs path
        if let Some(logs_path) = matches.value_of("LOGS_PATH") {
            dbuilder.logs_path(PathBuf::from(logs_path));
        }

        dbuilder.skip_if_exists(matches.is_present("SKIP_IF_EXISTS"));
        dbuilder.skip_if_log_exists(matches.is_present("SKIP_IF_LOG_EXISTS"));
        dbuilder.keep_build_directory(matches.is_present("KEEP_BUILD_DIRECTORY"));

        // check paths
        if let Err(e) = dbuilder.check_paths() {
            println!("{:?}\nUse --help to get more information", e);
            std::process::exit(1);
        }

        // build world
        if let Some(matches) = matches.subcommand_matches("world") {
            dbuilder.build_only_latest_version(matches.is_present("BUILD_ONLY_LATEST_VERSION"));
            dbuilder.skip_oldest_versions(matches.is_present("SKIP_OLDEST_VERSIONS"));
            if let Err(e) = dbuilder.build_doc_for_every_crate() {
                println!("Failed to build world: {:#?}", e);
            }
        } else if let Some(matches) = matches.subcommand_matches("crate") {
            // Safe to call unwrap here
            let crte_name = matches.value_of("CRATE_NAME").unwrap();
            let version = matches.value_of("CRATE_VERSION").unwrap();
            let crte = Crate::new(crte_name.to_string(), vec![version.to_string()]);

            if let Err(e) = dbuilder.build_doc_for_crate_version(&crte, 0) {
                match e {
                    DocBuilderError::SkipDocumentationExists => {
                        println!("Skipping {} documentation already exists",
                                 crte.canonical_name(0))
                    }
                    _ => {
                        println!("Failed to build documentation for {}: {:?}",
                                 crte.canonical_name(0),
                                 e)
                    }
                }
            }
        } else if let Some(_) = matches.subcommand_matches("download-sources") {
            if let Err(e) = dbuilder.download_sources() {
                println!("{:?}", e);
            }
        }
    }

    // build-doc
    else if let Some(matches) = matches.subcommand_matches("build-doc") {
        let mut docbuilder = DocBuilder::default();
        let mut crates_io_index_path;

        // set crates.io-index path
        if let Some(crates_io_index_path_conf) = matches.value_of("CRATES_IO_INDEX_PATH") {
            crates_io_index_path = PathBuf::from(crates_io_index_path_conf);
            docbuilder.crates_io_index_path(PathBuf::from(&crates_io_index_path));
        } else {
            crates_io_index_path = env::home_dir().unwrap();
            crates_io_index_path.push(".crates.io-index");
            docbuilder.crates_io_index_path(PathBuf::from(&crates_io_index_path));
        }

        // update crates.io-index path
        if let Err(e) = update_crates_io_index(&crates_io_index_path) {
            panic!("{}", e);
        }

        // crates.io-index required for single crate
        if let Err(e) = docbuilder.check_crates_io_index_path() {
            panic!("{:?}", e);
        }

        let crte_name = matches.value_of("CRATE_NAME").unwrap();
        let version = matches.value_of("CRATE_VERSION").unwrap();

        let crte = Crate::new(crte_name.to_string(), vec![version.to_string()]);

        if matches.is_present("CLEAN") {
            clean_build_dir().unwrap();
        }

        if let Err(e) = crte.build_crate_doc(0, &docbuilder) {
            error!("Failed to build crate\n{:?}", e);
            exit(1);
        } else {
            info!("Crate successfully built!");
        }
    }

}
<commit_msg>More idiomatic for crates_io_index_path<commit_after>
extern crate cratesfyi;
extern crate clap;
#[macro_use]
extern crate log;
extern crate time;

use std::env;
use std::fs;
use std::process::{Command, exit};
use std::path::PathBuf;

use cratesfyi::docbuilder::{DocBuilder, DocBuilderError, command_result};
use cratesfyi::docbuilder::crte::Crate;
use clap::{Arg, App, SubCommand};
use log::{LogLevel, LogLevelFilter, LogRecord, LogMetadata};
use time::now;

// I am using really simple Logger in this program
struct Logger;

impl log::Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            println!("{} {} - {}", now().to_utc().rfc3339(), record.level(), record.args());
        }
    }
}

fn update_crates_io_index(path: &PathBuf) -> Result<String, String> {
    info!("Updating crates.io-index");

    if path.exists() {
        let cwd = env::current_dir().unwrap();
        env::set_current_dir(path).unwrap();
        let res = command_result(Command::new("git").arg("pull").output().unwrap());
        env::set_current_dir(cwd).unwrap();
        res
    } else {
        command_result(Command::new("git")
            .arg("clone")
            .arg("https://github.com/rust-lang/crates.io-index.git")
            .arg(path.to_str().unwrap())
            .output().unwrap())
    }
}

// This will remove everything in CWD!!!
fn clean_build_dir() -> Result<(), DocBuilderError> {

    for file in try!(env::current_dir().unwrap()
                     .read_dir().map_err(DocBuilderError::RemoveBuildDir)) {
        let file = try!(file.map_err(DocBuilderError::RemoveBuildDir));
        let path = file.path();

        if path.file_name().unwrap() == ".cargo" ||
            path.file_name().unwrap() == ".crates.io-index" {
                continue;
            }

        if path.is_dir() {
            try!(fs::remove_dir_all(path).map_err(DocBuilderError::RemoveBuildDir));
        } else {
            try!(fs::remove_file(path).map_err(DocBuilderError::RemoveBuildDir));
        }
    }

    Ok(())
}

fn main() {
    log::set_logger(|max_log_level| {
        max_log_level.set(LogLevelFilter::Info);
        Box::new(Logger)
    }).unwrap();

    let matches = App::new("cratesfyi")
                      .version(env!("CARGO_PKG_VERSION"))
                      .about("Crates for your info!")
                      .subcommand(SubCommand::with_name("build")
                                      .about("Builds documentation in a chroot environment")
                                      .arg(Arg::with_name("PREFIX")
                                               .short("P")
                                               .long("prefix")
                                               .takes_value(true))
                                      .arg(Arg::with_name("DESTINATION")
                                               .short("d")
                                               .long("destination")
                                               .help("Sets destination path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_PATH")
                                               .short("c")
                                               .long("chroot")
                                               .help("Sets chroot path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CHROOT_USER")
                                               .short("u")
                                               .long("chroot-user")
                                               .help("Sets chroot user name")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CRATES_IO_INDEX_PATH")
                                               .long("crates-io-index-path")
                                               .help("Sets crates.io-index path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("LOGS_PATH")
                                               .long("logs-path")
                                               .help("Sets logs path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("SKIP_IF_EXISTS")
                                               .short("s")
                                               .long("skip")
                                               .help("Skips building documentation if \
                                                      documentation exists"))
                                      .arg(Arg::with_name("SKIP_IF_LOG_EXISTS")
                                               .long("skip-if-log-exists")
                                               .help("Skips building documentation if build \
                                                      log exists"))
                                      .arg(Arg::with_name("KEEP_BUILD_DIRECTORY")
                                               .short("-k")
                                               .long("keep-build-directory")
                                               .help("Keeps build directory after build."))
                                      .subcommand(SubCommand::with_name("download-sources")
                                                      .about("Downloads sources of all crates"))
                                      .subcommand(SubCommand::with_name("world")
                                                      .about("Builds documentation of every \
                                                              crate")
                                                      .arg(Arg::with_name("SKIP_OLDEST_VERSIONS")
                                                               .long("skip-oldest-versions")
                                                               .help("Skips trying to build \
                                                                      oldest versions \
                                                                      if build fails"))
                                                      .arg(Arg::with_name("BUILD_ONLY_LATEST_V\
                                                                           ERSION")
                                                               .long("build-only-latest-versio\
                                                                      n")
                                                               .help("Builds only latest \
                                                                      version of crate and \
                                                                      skips oldest versions")))
                                      .subcommand(SubCommand::with_name("crate")
                                                      .about("Builds documentation for a crate")
                                                      .arg(Arg::with_name("CRATE_NAME")
                                                               .index(1)
                                                               .required(true)
                                                               .help("Crate name"))
                                                      .arg(Arg::with_name("CRATE_VERSION")
                                                               .index(2)
                                                               .required(true)
                                                               .help("Version of crate"))))
                      .subcommand(SubCommand::with_name("build-doc")
                                      .about("Builds documentation in CWD")
                                      .arg(Arg::with_name("CRATES_IO_INDEX_PATH")
                                               .short("p")
                                               .long("crates-io-index-path")
                                               .help("Sets crates.io-index path")
                                               .takes_value(true))
                                      .arg(Arg::with_name("CLEAN")
                                               .short("c")
                                               .long("clean")
                                               .help("Clean build dir before building"))
                                      .arg(Arg::with_name("CRATE_NAME")
                                               .index(1)
                                               .required(true)
                                               .help("Crate name"))
                                      .arg(Arg::with_name("CRATE_VERSION")
                                               .index(2)
                                               .required(true)
                                               .help("Version of crate")))
                      .get_matches();

    // DocBuilder
    if let Some(matches) = matches.subcommand_matches("build") {
        let mut dbuilder = {
            if let Some(prefix) = matches.value_of("PREFIX") {
                DocBuilder::from_prefix(PathBuf::from(prefix))
            } else {
                DocBuilder::default()
            }
        };

        // set destination
        if let Some(destination) = matches.value_of("DESTINATION") {
            dbuilder.destination(PathBuf::from(destination));
        }

        // set chroot path
        if let Some(chroot_path) = matches.value_of("CHROOT_PATH") {
            dbuilder.destination(PathBuf::from(chroot_path));
        }

        // set chroot user name
        if let Some(chroot_user) = matches.value_of("CHROOT_USER") {
            dbuilder.chroot_user(chroot_user.to_string());
        }

        // set crates.io-index path
        if let Some(crates_io_index_path) = matches.value_of("CRATES_IO_INDEX_PATH") {
            dbuilder.crates_io_index_path(PathBuf::from(crates_io_index_path));
        }

        // set logs path
        if let Some(logs_path) = matches.value_of("LOGS_PATH") {
            dbuilder.logs_path(PathBuf::from(logs_path));
        }

        dbuilder.skip_if_exists(matches.is_present("SKIP_IF_EXISTS"));
        dbuilder.skip_if_log_exists(matches.is_present("SKIP_IF_LOG_EXISTS"));
        dbuilder.keep_build_directory(matches.is_present("KEEP_BUILD_DIRECTORY"));

        // check paths
        if let Err(e) = dbuilder.check_paths() {
            println!("{:?}\nUse --help to get more information", e);
            std::process::exit(1);
        }

        // build world
        if let Some(matches) = matches.subcommand_matches("world") {
            dbuilder.build_only_latest_version(matches.is_present("BUILD_ONLY_LATEST_VERSION"));
            dbuilder.skip_oldest_versions(matches.is_present("SKIP_OLDEST_VERSIONS"));
            if let Err(e) = dbuilder.build_doc_for_every_crate() {
                println!("Failed to build world: {:#?}", e);
            }
        } else if let Some(matches) = matches.subcommand_matches("crate") {
            // Safe to call unwrap here
            let crte_name = matches.value_of("CRATE_NAME").unwrap();
            let version = matches.value_of("CRATE_VERSION").unwrap();
            let crte = Crate::new(crte_name.to_string(), vec![version.to_string()]);

            if let Err(e) = dbuilder.build_doc_for_crate_version(&crte, 0) {
                match e {
                    DocBuilderError::SkipDocumentationExists => {
                        println!("Skipping {} documentation already exists",
                                 crte.canonical_name(0))
                    }
                    _ => {
                        println!("Failed to build documentation for {}: {:?}",
                                 crte.canonical_name(0),
                                 e)
                    }
                }
            }
        } else if let Some(_) = matches.subcommand_matches("download-sources") {
            if let Err(e) = dbuilder.download_sources() {
                println!("{:?}", e);
            }
        }
    }

    // build-doc
    else if let Some(matches) = matches.subcommand_matches("build-doc") {
        let mut docbuilder = DocBuilder::default();

        // set crates.io-index path
        let crates_io_index_path = {
            if let Some(crates_io_index_path_conf) = matches.value_of("CRATES_IO_INDEX_PATH") {
                PathBuf::from(crates_io_index_path_conf)
            } else {
                let mut path = env::home_dir().unwrap();
                path.push(".crates.io-index");
                path
            }
        };

        docbuilder.crates_io_index_path(PathBuf::from(&crates_io_index_path));

        // update crates.io-index path
        if let Err(e) = update_crates_io_index(&crates_io_index_path) {
            panic!("{}", e);
        }

        // crates.io-index required for single crate
        if let Err(e) = docbuilder.check_crates_io_index_path() {
            panic!("{:?}", e);
        }

        let crte_name = matches.value_of("CRATE_NAME").unwrap();
        let version = matches.value_of("CRATE_VERSION").unwrap();

        let crte = Crate::new(crte_name.to_string(), vec![version.to_string()]);

        if matches.is_present("CLEAN") {
            clean_build_dir().unwrap();
        }

        if let Err(e) = crte.build_crate_doc(0, &docbuilder) {
            error!("Failed to build crate\n{:?}", e);
            exit(1);
        } else {
            info!("Crate successfully built!");
        }
    }

}
<|endoftext|>


<commit_before>use std::fmt;
use rand::{Rng, thread_rng};

///
/// Abstraction for a single Entry for our log.
///
#[derive(Clone)]
pub struct Entry {
    pub index: usize,   // index of this Entry in the log
    pub term: u64,      // term for which this Entry is committed
    pub data: Vec<u8>,  // data blob containing client-defined command to
                        // apply to the client state machine.
}

#[cfg(test)]
fn random_entry_with_metadata(term: u64, index: usize) -> Entry {
  let mut vec = vec![0; 8];
  thread_rng().fill_bytes(&mut vec);
  Entry {
      index: index,
      term: term,
      data: vec,
  }
}

#[cfg(test)]
pub fn random_entry() -> Entry {
    random_entry_with_metadata(0, 0)
}

impl fmt::Debug for Entry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Entry {{ term: {}, data: {:?} }}", self.term, self.data)
    }
}

impl PartialEq for Entry {
    fn eq(&self, other: &Entry) -> bool {
        self.term == other.term && self.data == other.data
    }
}

///
/// Abstraction for a log of "commands" to apply to the client state machine,
/// stored locally.
/// Small wrapper over a list of Entries-- eventually should also abstract away
/// automatically pushing to persistent storage / snapshotting (TODO)
///
pub trait Log: Sync + Send {
    ///
    /// Retrieves read-only reference to an Entry in the log, specified by |index|.
    /// Will panic if supplied an index exceeds the log length.
    ///
    fn get_entry(&self, index: usize) -> &Entry;

    ///
    /// Retrieves read-only reference to a slice of all Entries in the log past |start_index|.
    /// Will panic if supplied an index exceeds the log length.
    ///
    fn get_entries_from(&self, start_index: usize) -> &[Entry];

    ///
    /// Appends a copy of each entry in |entries| to log. Will correctly modify
    /// |index| field in each Entry to match its index in the log.
    /// Returns this log object.
    ///
    fn append_entries(&mut self, entries: Vec<Entry>) -> &Log;

    ///
    /// Appends a copy of |entry| to the log. Will correclty modify
    /// |index| field in |entry| to match its index in the log.
    /// Returns this log object.
    ///
    fn append_entry(&mut self, entry: Entry) -> &Log;

    ///
    /// Retrieves the index of the last entry in our log.
    ///
    fn get_last_entry_index(&self) -> usize;

    ///
    /// Retrieves the term of the last entry in our log.
    ///
    fn get_last_entry_term(&self) -> u64;

    ///
    /// Rolls back log so that the most recent entry is located at |index|.
    /// Panics if |index| is an invalid log index.
    /// Returns this log object.
    ///
    fn roll_back(&mut self, index: usize) -> &Log;

    ///
    /// Returns true if the log represented by other_log_index,other_log_term is at least as
    /// complete as this log object.
    ///
    /// Completeness is determined by whichever log has a later term. If both logs have the same
    /// last commited term, then whichever log's index is larger is considered more complete
    ///
    fn is_other_log_valid(&self, other_log_index: usize, other_log_term: u64) -> bool;
}

///
/// Simple implementation of Log that stores all logs in memory.
///
pub struct MemoryLog {
    entries: Vec<Entry>,
}

impl MemoryLog {
    pub fn new() -> MemoryLog {
        MemoryLog { entries: Vec::new() }
    }
}

impl Log for MemoryLog {
    fn get_entry(&self, index: usize) -> &Entry {
        self.entries.get(index).unwrap()
    }

    fn get_entries_from(&self, start_index: usize) -> &[Entry] {
        &self.entries[start_index .. self.entries.len()]
    }

    fn append_entries(&mut self, entries: Vec<Entry>) -> &Log {
        let mut start_index = self.entries.len();
        let indexed_entries = entries.into_iter().map(|mut entry| {
            entry.index = start_index;
            start_index += 1;
            entry
        }).collect::<Vec<Entry>>();
        self.entries.extend(indexed_entries);
        self
    }

    fn append_entry(&mut self, mut entry: Entry) -> &Log {
        entry.index = self.entries.len();
        self.entries.push(entry);
        self
    }

    fn get_last_entry_index(&self) -> usize {
        self.entries.len() - 1 as usize
    }

    fn get_last_entry_term(&self) -> u64 {
        self.get_entry(self.get_last_entry_index()).term
    }

    fn roll_back(&mut self, index: usize) -> &Log {
        self.entries.drain(index + 1..).collect::<Vec<_>>();
        self
    }

    // TODO(jason): Add unit tests
    fn is_other_log_valid (&self, other_log_index: usize, other_log_term: u64) -> bool {
        let last_log_term = self.get_last_entry_term();
        other_log_term > last_log_term ||
            (other_log_term == last_log_term &&
            other_log_index >= self.get_last_entry_index())
    }
}

/// Tests for the log-- these tests should pass for any Log (to test any log, just change the
/// type returned by the |create_log()| helper.
#[cfg(test)]
mod tests {
    use super::{Log, MemoryLog, random_entry, random_entry_with_metadata};
    fn create_log() -> MemoryLog { MemoryLog::new() } // Replace with other log types as needed

    fn create_filled_log(length: usize) -> MemoryLog {
        let mut log = create_log();
        log.append_entries( vec![random_entry(); length] );
        log
    }

    #[test]
    #[should_panic]
    /// Makes sure we panic if an incorrect index is supplied to the log.
    fn test_log_get_entry_out_of_index() {
        let mut log = create_log();
        log.append_entry(random_entry());
        log.get_entry(1); // should panic
    }

    #[test]
    /// Tests that simple append and get works for a two-element array.
    fn test_log_append_get_simple() {
        let mut log = create_log();
        let entry1 = random_entry();
        let entry2 = random_entry();

        // Append operations
        log.append_entry(entry1.clone());
        log.append_entry(entry2.clone());

        // Data should be the same
        assert_eq!(entry1.data, log.get_entry(0).data);
        assert_eq!(entry2.data, log.get_entry(1).data);
    }

    #[test]
    /// Tests that indices stay valid after appending entries.
    fn test_log_append_indices_simple() {
        let length = 10;
        let log = create_filled_log(length);
        // Make sure the indices are correctly set
        for i in 0..length {
            assert_eq!(log.get_entry(i).index, i);
        }
    }

    #[test]
    /// Tests that indices stay valid after appending entries.
    fn test_log_roll_back_simple() {
        let length = 10;
        let mut log = create_filled_log(length);
        let new_length = 5;
        log.roll_back(new_length);
        // make sure log has been rolled back!
        assert_eq!(log.get_last_entry_index(), new_length);
    }

    #[test]
    fn is_other_log_valid_rejects_previous_terms() {
        let mut log = create_log();
        log.append_entry(random_entry_with_metadata(0, 0));
        log.append_entry(random_entry_with_metadata(1, 1));
        assert!(!log.is_other_log_valid(2, 0));
    }

    #[test]
    fn is_other_log_valid_rejects_lower_indices() {
        let mut log = create_log();
        log.append_entry(random_entry_with_metadata(0, 0));
        log.append_entry(random_entry_with_metadata(1, 1));
        log.append_entry(random_entry_with_metadata(2, 1));
        assert!(!log.is_other_log_valid(1, 1));
    }

    #[test]
    fn is_other_log_valid_accepts_same_index() {
        let mut log = create_log();
        log.append_entry(random_entry_with_metadata(0, 0));
        assert!(log.is_other_log_valid(0, 0));
    }

    #[test]
    #[ignore]
    /// TODO (sydli): Generate a bunch of random queries on the log and make
    ///               sure it stays consistent.
    fn test_fuzz() {
        unimplemented!();
    }
}
<commit_msg>feat(log): Make log indices relative to a start index<commit_after>use std::fmt;
use rand::{Rng, thread_rng};

///
/// Abstraction for a single Entry for our log.
///
#[derive(Clone)]
pub struct Entry {
    pub index: usize,   // index of this Entry in the log
    pub term: u64,      // term for which this Entry is committed
    pub data: Vec<u8>,  // data blob containing client-defined command to
                        // apply to the client state machine.
}

#[cfg(test)]
fn random_entry_with_term(term: u64) -> Entry {
  assert_ne!(term, 0);

  let mut vec = vec![0; 8];
  thread_rng().fill_bytes(&mut vec);
  Entry {
      index: 0,
      term: term,
      data: vec,
  }
}

#[cfg(test)]
pub fn random_entry() -> Entry {
    random_entry_with_term(1)
}

impl fmt::Debug for Entry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Entry {{ term: {}, data: {:?} }}", self.term, self.data)
    }
}

impl PartialEq for Entry {
    fn eq(&self, other: &Entry) -> bool {
        self.term == other.term && self.data == other.data
    }
}

///
/// Abstraction for a log of "commands" to apply to the client state machine,
/// stored locally.
/// Small wrapper over a list of Entries-- eventually should also abstract away
/// automatically pushing to persistent storage / snapshotting (TODO)
///
pub trait Log: Sync + Send {
    ///
    /// Retrieves read-only reference to an Entry in the log, specified by |index|.
    /// Will panic if supplied an index exceeds the log length.
    ///
    fn get_entry(&self, index: usize) -> Option<&Entry>;

    ///
    /// Retrieves read-only reference to a slice of all Entries in the log past |start_index|.
    /// Will panic if supplied an index exceeds the log length.
    ///
    fn get_entries_from(&self, start_index: usize) -> &[Entry];

    ///
    /// Appends a copy of each entry in |entries| to log. Will correctly modify
    /// |index| field in each Entry to match its index in the log.
    /// Returns this log object.
    ///
    fn append_entries(&mut self, entries: Vec<Entry>) -> &Log;

    ///
    /// Appends a copy of |entry| to the log. Will correclty modify
    /// |index| field in |entry| to match its index in the log.
    /// Returns this log object.
    ///
    fn append_entry(&mut self, entry: Entry) -> &Log;

    ///
    /// Retrieves the index of the last entry in our log.
    ///
    fn get_last_entry_index(&self) -> usize;

    ///
    /// Retrieves the term of the last entry in our log.
    ///
    fn get_last_entry_term(&self) -> u64;

    ///
    /// Rolls back log so that the most recent entry is located at |index|.
    /// Panics if |index| is an invalid log index.
    /// Returns this log object.
    ///
    fn roll_back(&mut self, index: usize) -> &Log;

    ///
    /// Returns true if the log represented by other_log_index,other_log_term is at least as
    /// complete as this log object.
    ///
    /// Completeness is determined by whichever log has a later term. If both logs have the same
    /// last commited term, then whichever log's index is larger is considered more complete
    ///
    fn is_other_log_valid(&self, other_log_index: usize, other_log_term: u64) -> bool;
}

///
/// Simple implementation of Log that stores all logs in memory.
///
pub struct MemoryLog {
    entries: Vec<Entry>,
    start_index: usize
}

impl MemoryLog {
    pub fn new() -> MemoryLog {
        // start index is always 1, but will be dynamic after we implement snapshotting
        MemoryLog { entries: Vec::new(), start_index: 1}
    }
}

impl Log for MemoryLog {
    fn get_entry(&self, index: usize) -> Option<&Entry> {
        if index < self.start_index {
            return None;
        }

        self.entries.get(index - self.start_index)
    }

    // TODO(jason)
    fn get_entries_from(&self, start_index: usize) -> &[Entry] {
        &self.entries[start_index .. self.entries.len()]
    }

    fn append_entries(&mut self, entries: Vec<Entry>) -> &Log {
        let mut start_index = self.entries.len() + self.start_index;
        let indexed_entries = entries.into_iter().map(|mut entry| {
            debug_assert!(entry.term > 0); // can't commit in term 0
            entry.index = start_index;
            start_index += 1;
            entry
        }).collect::<Vec<Entry>>();
        self.entries.extend(indexed_entries);
        self
    }

    fn append_entry(&mut self, mut entry: Entry) -> &Log {
        debug_assert!(entry.term > 0); // can't commit in term 0
        entry.index = self.entries.len() + self.start_index;
        self.entries.push(entry);
        self
    }

    fn get_last_entry_index(&self) -> usize {
        self.entries.len() - (self.start_index - 1)
    }

    fn get_last_entry_term(&self) -> u64 {
        if self.entries.len() == 0 {
            return 0;
        }

        self.get_entry(self.get_last_entry_index())
            .map_or(0, |e| e.term)
    }

    ///
    /// Deletes all entries > index from the log
    ///
    /// #Panics
    /// Panics if you try to roll back entries that are < start_index
    /// because you should never try to roll back snapshotted entries
    ///
    fn roll_back(&mut self, index: usize) -> &Log {
        let start_index = index - self.start_index + 1;
        if start_index >= self.entries.len() {
            return self; // nothing to remove
        }

        self.entries.drain(start_index ..).collect::<Vec<_>>();
        self
    }

    fn is_other_log_valid (&self, other_log_index: usize, other_log_term: u64) -> bool {
        let last_log_term = self.get_last_entry_term();
        other_log_term > last_log_term ||
            (other_log_term == last_log_term &&
            other_log_index >= self.get_last_entry_index())
    }
}

/// Tests for the log-- these tests should pass for any Log (to test any log, just change the
/// type returned by the |create_log()| helper.
#[cfg(test)]
mod tests {
    use super::{Log, MemoryLog, random_entry, random_entry_with_term};
    fn create_log() -> MemoryLog { MemoryLog::new() } // Replace with other log types as needed

    fn create_filled_log(length: usize) -> MemoryLog {
        let mut log = create_log();
        log.append_entries( vec![random_entry(); length] );
        log
    }

    #[test]
    /// Makes sure we return None if the index is out of the log
    fn test_log_get_entry_out_of_index() {
        let mut log = create_log();
        log.append_entry(random_entry());
        assert!(log.get_entry(2).is_none());
    }

    #[test]
    fn test_log_0_is_none() {
        let mut log = create_log();
        assert!(log.get_entry(0).is_none());
        log.append_entry(random_entry());
        assert!(log.get_entry(0).is_none());
    }

    #[test]
    /// Tests that simple append and get works for a two-element array.
    fn test_log_append_get_simple() {
        let mut log = create_log();
        let entry1 = random_entry();
        let entry2 = random_entry();

        // Append operations
        log.append_entry(entry1.clone());
        log.append_entry(entry2.clone());

        // Data should be the same
        assert_eq!(entry1.data, log.get_entry(1).unwrap().data);
        assert_eq!(entry2.data, log.get_entry(2).unwrap().data);
    }

    #[test]
    /// Tests that indices stay valid after appending entries.
    fn test_log_append_indices_simple() {
        let length = 10;
        let log = create_filled_log(length);
        // Make sure the indices are correctly set
        for i in 1..length + 1 {
            assert_eq!(log.get_entry(i).unwrap().index, i);
        }
    }

    #[test]
    /// Tests that indices stay valid after appending entries.
    fn test_log_roll_back_simple() {
        let length = 10;
        let mut log = create_filled_log(length);
        let new_length = 5;
        log.roll_back(new_length);
        // make sure log has been rolled back!
        assert_eq!(log.get_last_entry_index(), new_length);
    }

    #[test]
    fn log_roll_back_works_with_nothing_to_roll_back() {
        let mut log = create_log();
        log.roll_back(1);
    }

    #[test]
    fn is_other_log_valid_rejects_previous_terms() {
        let mut log = create_log();
        log.append_entry(random_entry_with_term(1));
        log.append_entry(random_entry_with_term(2));
        assert!(!log.is_other_log_valid(3, 1));
    }

    #[test]
    fn is_other_log_valid_rejects_lower_indices() {
        let mut log = create_log();
        log.append_entry(random_entry_with_term(1));
        log.append_entry(random_entry_with_term(1));
        log.append_entry(random_entry_with_term(2));
        assert!(!log.is_other_log_valid(1, 2));
    }

    #[test]
    fn is_other_log_valid_accepts_same_index() {
        let mut log = create_log();
        log.append_entry(random_entry_with_term(1));
        assert!(log.is_other_log_valid(1, 1));
    }

    #[test]
    fn is_other_log_valid_accepts_with_empty_log() {
        let mut log = create_log();
        assert!(log.is_other_log_valid(0, 0));
    }

    #[test]
    #[ignore]
    /// TODO (sydli): Generate a bunch of random queries on the log and make
    ///               sure it stays consistent.
    fn test_fuzz() {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>use crate::fmt::display;
use crate::kind::Trivial;
use crate::string::CxxString;
use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
use crate::ExternType;
use core::ffi::c_void;
use core::fmt::{self, Debug, Display};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::Deref;

/// Binding to C++ `std::shared_ptr<T>`.
#[repr(C)]
pub struct SharedPtr<T>
where
    T: SharedPtrTarget,
{
    repr: [MaybeUninit<*mut c_void>; 2],
    ty: PhantomData<T>,
}

impl<T> SharedPtr<T>
where
    T: SharedPtrTarget,
{
    /// Makes a new SharedPtr wrapping a null pointer.
    ///
    /// Matches the behavior of default-constructing a std::shared\_ptr.
    pub fn null() -> Self {
        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
        let new = shared_ptr.as_mut_ptr().cast();
        unsafe {
            T::__null(new);
            shared_ptr.assume_init()
        }
    }

    /// Allocates memory on the heap and makes a SharedPtr owner for it.
    pub fn new(value: T) -> Self
    where
        T: ExternType<Kind = Trivial>,
    {
        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
        let new = shared_ptr.as_mut_ptr().cast();
        unsafe {
            T::__new(value, new);
            shared_ptr.assume_init()
        }
    }

    /// Checks whether the SharedPtr does not own an object.
    ///
    /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
    pub fn is_null(&self) -> bool {
        let this = self as *const Self as *const c_void;
        let ptr = unsafe { T::__get(this) };
        ptr.is_null()
    }

    /// Returns a reference to the object owned by this SharedPtr if any,
    /// otherwise None.
    pub fn as_ref(&self) -> Option<&T> {
        let this = self as *const Self as *const c_void;
        unsafe { T::__get(this).as_ref() }
    }

    /// Constructs new WeakPtr as a non-owning reference to the object managed
    /// by `self`. If `self` manages no object, the WeakPtr manages no object
    /// too.
    ///
    /// Matches the behavior of [std::weak_ptr\<T\>::weak_ptr(const std::shared_ptr\<T\> \&)](https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr).
    pub fn downgrade(self: &SharedPtr<T>) -> WeakPtr<T>
    where
        T: WeakPtrTarget,
    {
        let this = self as *const Self as *const c_void;
        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
        let new = weak_ptr.as_mut_ptr().cast();
        unsafe {
            T::__downgrade(this, new);
            weak_ptr.assume_init()
        }
    }
}

unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}

impl<T> Clone for SharedPtr<T>
where
    T: SharedPtrTarget,
{
    fn clone(&self) -> Self {
        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
        let new = shared_ptr.as_mut_ptr().cast();
        let this = self as *const Self as *mut c_void;
        unsafe {
            T::__clone(this, new);
            shared_ptr.assume_init()
        }
    }
}

impl<T> Drop for SharedPtr<T>
where
    T: SharedPtrTarget,
{
    fn drop(&mut self) {
        let this = self as *mut Self as *mut c_void;
        unsafe { T::__drop(this) }
    }
}

impl<T> Deref for SharedPtr<T>
where
    T: SharedPtrTarget,
{
    type Target = T;

    fn deref(&self) -> &Self::Target {
        match self.as_ref() {
            Some(target) => target,
            None => panic!(
                "called deref on a null SharedPtr<{}>",
                display(T::__typename),
            ),
        }
    }
}

impl<T> Debug for SharedPtr<T>
where
    T: Debug + SharedPtrTarget,
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.as_ref() {
            None => formatter.write_str("nullptr"),
            Some(value) => Debug::fmt(value, formatter),
        }
    }
}

impl<T> Display for SharedPtr<T>
where
    T: Display + SharedPtrTarget,
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.as_ref() {
            None => formatter.write_str("nullptr"),
            Some(value) => Display::fmt(value, formatter),
        }
    }
}

/// Trait bound for types which may be used as the `T` inside of a
/// `SharedPtr<T>` in generic code.
///
/// This trait has no publicly callable or implementable methods. Implementing
/// it outside of the CXX codebase is not supported.
///
/// # Example
///
/// A bound `T: SharedPtrTarget` may be necessary when manipulating
/// [`SharedPtr`] in generic code.
///
/// ```
/// use cxx::memory::{SharedPtr, SharedPtrTarget};
/// use std::fmt::Display;
///
/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
/// where
///     T: SharedPtrTarget + Display,
/// {
///     println!("the shared_ptr points to: {}", *ptr);
/// }
/// ```
///
/// Writing the same generic function without a `SharedPtrTarget` trait bound
/// would not compile.
pub unsafe trait SharedPtrTarget {
    #[doc(hidden)]
    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
    #[doc(hidden)]
    unsafe fn __null(new: *mut c_void);
    #[doc(hidden)]
    unsafe fn __new(value: Self, new: *mut c_void)
    where
        Self: Sized,
    {
        // Opoaque C types do not get this method because they can never exist
        // by value on the Rust side of the bridge.
        let _ = value;
        let _ = new;
        unreachable!()
    }
    #[doc(hidden)]
    unsafe fn __clone(this: *const c_void, new: *mut c_void);
    #[doc(hidden)]
    unsafe fn __get(this: *const c_void) -> *const Self;
    #[doc(hidden)]
    unsafe fn __drop(this: *mut c_void);
}

macro_rules! impl_shared_ptr_target {
    ($segment:expr, $name:expr, $ty:ty) => {
        unsafe impl SharedPtrTarget for $ty {
            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
                f.write_str($name)
            }
            unsafe fn __null(new: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
                        fn __null(new: *mut c_void);
                    }
                }
                unsafe { __null(new) }
            }
            unsafe fn __new(value: Self, new: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
                        fn __uninit(new: *mut c_void) -> *mut c_void;
                    }
                }
                unsafe { __uninit(new).cast::<$ty>().write(value) }
            }
            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
                        fn __clone(this: *const c_void, new: *mut c_void);
                    }
                }
                unsafe { __clone(this, new) }
            }
            unsafe fn __get(this: *const c_void) -> *const Self {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
                        fn __get(this: *const c_void) -> *const c_void;
                    }
                }
                unsafe { __get(this) }.cast()
            }
            unsafe fn __drop(this: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
                        fn __drop(this: *mut c_void);
                    }
                }
                unsafe { __drop(this) }
            }
        }
    };
}

macro_rules! impl_shared_ptr_target_for_primitive {
    ($ty:ident) => {
        impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
    };
}

impl_shared_ptr_target_for_primitive!(bool);
impl_shared_ptr_target_for_primitive!(u8);
impl_shared_ptr_target_for_primitive!(u16);
impl_shared_ptr_target_for_primitive!(u32);
impl_shared_ptr_target_for_primitive!(u64);
impl_shared_ptr_target_for_primitive!(usize);
impl_shared_ptr_target_for_primitive!(i8);
impl_shared_ptr_target_for_primitive!(i16);
impl_shared_ptr_target_for_primitive!(i32);
impl_shared_ptr_target_for_primitive!(i64);
impl_shared_ptr_target_for_primitive!(isize);
impl_shared_ptr_target_for_primitive!(f32);
impl_shared_ptr_target_for_primitive!(f64);

impl_shared_ptr_target!("string", "CxxString", CxxString);
<commit_msg>Make SharedPtr Unpin like UniquePtr<commit_after>use crate::fmt::display;
use crate::kind::Trivial;
use crate::string::CxxString;
use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
use crate::ExternType;
use core::ffi::c_void;
use core::fmt::{self, Debug, Display};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
use core::ops::Deref;

/// Binding to C++ `std::shared_ptr<T>`.
#[repr(C)]
pub struct SharedPtr<T>
where
    T: SharedPtrTarget,
{
    repr: [MaybeUninit<*mut c_void>; 2],
    ty: PhantomData<T>,
}

impl<T> SharedPtr<T>
where
    T: SharedPtrTarget,
{
    /// Makes a new SharedPtr wrapping a null pointer.
    ///
    /// Matches the behavior of default-constructing a std::shared\_ptr.
    pub fn null() -> Self {
        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
        let new = shared_ptr.as_mut_ptr().cast();
        unsafe {
            T::__null(new);
            shared_ptr.assume_init()
        }
    }

    /// Allocates memory on the heap and makes a SharedPtr owner for it.
    pub fn new(value: T) -> Self
    where
        T: ExternType<Kind = Trivial>,
    {
        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
        let new = shared_ptr.as_mut_ptr().cast();
        unsafe {
            T::__new(value, new);
            shared_ptr.assume_init()
        }
    }

    /// Checks whether the SharedPtr does not own an object.
    ///
    /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
    pub fn is_null(&self) -> bool {
        let this = self as *const Self as *const c_void;
        let ptr = unsafe { T::__get(this) };
        ptr.is_null()
    }

    /// Returns a reference to the object owned by this SharedPtr if any,
    /// otherwise None.
    pub fn as_ref(&self) -> Option<&T> {
        let this = self as *const Self as *const c_void;
        unsafe { T::__get(this).as_ref() }
    }

    /// Constructs new WeakPtr as a non-owning reference to the object managed
    /// by `self`. If `self` manages no object, the WeakPtr manages no object
    /// too.
    ///
    /// Matches the behavior of [std::weak_ptr\<T\>::weak_ptr(const std::shared_ptr\<T\> \&)](https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr).
    pub fn downgrade(self: &SharedPtr<T>) -> WeakPtr<T>
    where
        T: WeakPtrTarget,
    {
        let this = self as *const Self as *const c_void;
        let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
        let new = weak_ptr.as_mut_ptr().cast();
        unsafe {
            T::__downgrade(this, new);
            weak_ptr.assume_init()
        }
    }
}

unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}

impl<T> Clone for SharedPtr<T>
where
    T: SharedPtrTarget,
{
    fn clone(&self) -> Self {
        let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
        let new = shared_ptr.as_mut_ptr().cast();
        let this = self as *const Self as *mut c_void;
        unsafe {
            T::__clone(this, new);
            shared_ptr.assume_init()
        }
    }
}

// SharedPtr is not a self-referential type and is safe to move out of a Pin,
// regardless whether the pointer's target is Unpin.
impl<T> Unpin for SharedPtr<T> where T: SharedPtrTarget {}

impl<T> Drop for SharedPtr<T>
where
    T: SharedPtrTarget,
{
    fn drop(&mut self) {
        let this = self as *mut Self as *mut c_void;
        unsafe { T::__drop(this) }
    }
}

impl<T> Deref for SharedPtr<T>
where
    T: SharedPtrTarget,
{
    type Target = T;

    fn deref(&self) -> &Self::Target {
        match self.as_ref() {
            Some(target) => target,
            None => panic!(
                "called deref on a null SharedPtr<{}>",
                display(T::__typename),
            ),
        }
    }
}

impl<T> Debug for SharedPtr<T>
where
    T: Debug + SharedPtrTarget,
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.as_ref() {
            None => formatter.write_str("nullptr"),
            Some(value) => Debug::fmt(value, formatter),
        }
    }
}

impl<T> Display for SharedPtr<T>
where
    T: Display + SharedPtrTarget,
{
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        match self.as_ref() {
            None => formatter.write_str("nullptr"),
            Some(value) => Display::fmt(value, formatter),
        }
    }
}

/// Trait bound for types which may be used as the `T` inside of a
/// `SharedPtr<T>` in generic code.
///
/// This trait has no publicly callable or implementable methods. Implementing
/// it outside of the CXX codebase is not supported.
///
/// # Example
///
/// A bound `T: SharedPtrTarget` may be necessary when manipulating
/// [`SharedPtr`] in generic code.
///
/// ```
/// use cxx::memory::{SharedPtr, SharedPtrTarget};
/// use std::fmt::Display;
///
/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
/// where
///     T: SharedPtrTarget + Display,
/// {
///     println!("the shared_ptr points to: {}", *ptr);
/// }
/// ```
///
/// Writing the same generic function without a `SharedPtrTarget` trait bound
/// would not compile.
pub unsafe trait SharedPtrTarget {
    #[doc(hidden)]
    fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
    #[doc(hidden)]
    unsafe fn __null(new: *mut c_void);
    #[doc(hidden)]
    unsafe fn __new(value: Self, new: *mut c_void)
    where
        Self: Sized,
    {
        // Opoaque C types do not get this method because they can never exist
        // by value on the Rust side of the bridge.
        let _ = value;
        let _ = new;
        unreachable!()
    }
    #[doc(hidden)]
    unsafe fn __clone(this: *const c_void, new: *mut c_void);
    #[doc(hidden)]
    unsafe fn __get(this: *const c_void) -> *const Self;
    #[doc(hidden)]
    unsafe fn __drop(this: *mut c_void);
}

macro_rules! impl_shared_ptr_target {
    ($segment:expr, $name:expr, $ty:ty) => {
        unsafe impl SharedPtrTarget for $ty {
            fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
                f.write_str($name)
            }
            unsafe fn __null(new: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
                        fn __null(new: *mut c_void);
                    }
                }
                unsafe { __null(new) }
            }
            unsafe fn __new(value: Self, new: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
                        fn __uninit(new: *mut c_void) -> *mut c_void;
                    }
                }
                unsafe { __uninit(new).cast::<$ty>().write(value) }
            }
            unsafe fn __clone(this: *const c_void, new: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
                        fn __clone(this: *const c_void, new: *mut c_void);
                    }
                }
                unsafe { __clone(this, new) }
            }
            unsafe fn __get(this: *const c_void) -> *const Self {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
                        fn __get(this: *const c_void) -> *const c_void;
                    }
                }
                unsafe { __get(this) }.cast()
            }
            unsafe fn __drop(this: *mut c_void) {
                extern "C" {
                    attr! {
                        #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
                        fn __drop(this: *mut c_void);
                    }
                }
                unsafe { __drop(this) }
            }
        }
    };
}

macro_rules! impl_shared_ptr_target_for_primitive {
    ($ty:ident) => {
        impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
    };
}

impl_shared_ptr_target_for_primitive!(bool);
impl_shared_ptr_target_for_primitive!(u8);
impl_shared_ptr_target_for_primitive!(u16);
impl_shared_ptr_target_for_primitive!(u32);
impl_shared_ptr_target_for_primitive!(u64);
impl_shared_ptr_target_for_primitive!(usize);
impl_shared_ptr_target_for_primitive!(i8);
impl_shared_ptr_target_for_primitive!(i16);
impl_shared_ptr_target_for_primitive!(i32);
impl_shared_ptr_target_for_primitive!(i64);
impl_shared_ptr_target_for_primitive!(isize);
impl_shared_ptr_target_for_primitive!(f32);
impl_shared_ptr_target_for_primitive!(f64);

impl_shared_ptr_target!("string", "CxxString", CxxString);
<|endoftext|>


<commit_before>// Copyright (C) 2020 Jason Ish
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use std::path::PathBuf;

use rusqlite::params;
use rusqlite::OpenFlags;

use crate::prelude::*;

pub mod configrepo;
pub mod eventstore;
pub mod importer;
pub mod queryparser;
pub mod retention;

pub async fn open_pool<T: Into<PathBuf>>(filename: T) -> anyhow::Result<deadpool_sqlite::Pool> {
    use deadpool_sqlite::{Config, Runtime};
    let config = Config::new(filename);
    let pool = config.create_pool(Runtime::Tokio1)?;
    let conn = pool.get().await?;
    if let Err(err) = conn
        .interact(|conn| {
            debug!("set journal mode to WAL");
            let mode = conn.pragma_update_and_check(None, "ournal_mode", &"WAL", |row| {
                let mode: String = row.get(0)?;
                Ok(mode)
            });
            info!("Result of setting database to WAL mode: {:?}", mode);

            // Set synchronous to NORMAL.
            if let Err(err) = conn.pragma_update(None, "synchronous", &"NORMAL") {
                error!("Failed to set pragma synchronous = NORMAL: {:?}", err);
            }
            match conn.pragma_query_value(None, "synchronous", |row| {
                let val: i32 = row.get(0)?;
                Ok(val)
            }) {
                Ok(mode) => {
                    if mode != 1 {
                        warn!("Database not in synchronous mode normal, instead: {}", mode);
                    }
                }
                Err(err) => {
                    warn!("Failed to query pragma synchronous: {:?}", err);
                }
            }
        })
        .await
    {
        return Err(anyhow::anyhow!("{:?}", err));
    }
    Ok(pool)
}

pub struct ConnectionBuilder {
    pub filename: Option<PathBuf>,
}

impl ConnectionBuilder {
    pub fn filename<T: Into<PathBuf>>(filename: Option<T>) -> ConnectionBuilder {
        ConnectionBuilder {
            filename: filename.map(|filename| filename.into()),
        }
    }

    pub fn open(&self) -> Result<rusqlite::Connection, rusqlite::Error> {
        let flags = OpenFlags::SQLITE_OPEN_READ_WRITE
            | OpenFlags::SQLITE_OPEN_CREATE
            | OpenFlags::SQLITE_OPEN_SHARED_CACHE
            | OpenFlags::SQLITE_OPEN_FULL_MUTEX;
        if let Some(filename) = &self.filename {
            let conn = rusqlite::Connection::open_with_flags(&filename, flags)?;

            // Set WAL mode.
            let mode = conn.pragma_update_and_check(None, "journal_mode", &"WAL", |row| {
                let mode: String = row.get(0)?;
                Ok(mode)
            });
            debug!("Result of setting database to WAL mode: {:?}", mode);

            // Set synchronous to NORMAL.
            if let Err(err) = conn.pragma_update(None, "synchronous", &"NORMAL") {
                error!("Failed to set pragma synchronous = NORMAL: {:?}", err);
            }
            match conn.pragma_query_value(None, "synchronous", |row| {
                let val: i32 = row.get(0)?;
                Ok(val)
            }) {
                Ok(mode) => {
                    if mode != 1 {
                        warn!("Database not in synchronous mode normal, instead: {}", mode);
                    }
                }
                Err(err) => {
                    warn!("Failed to query pragma synchronous: {:?}", err);
                }
            }

            Ok(conn)
        } else {
            rusqlite::Connection::open_in_memory()
        }
    }
}

pub fn init_event_db(db: &mut rusqlite::Connection) -> Result<(), rusqlite::Error> {
    let version = db
        .query_row("select max(version) from schema", params![], |row| {
            let version: i64 = row.get(0).unwrap();
            Ok(version)
        })
        .unwrap_or(-1);
    if version > -1 && version <= 3 {
        // We may have to provide the refinery table, unless it was already created.
        debug!("SQLite configuration DB at v1, checking if setup required for Refinery migrations");
        let fake_refinery_setup = "CREATE TABLE refinery_schema_history(
            version INT4 PRIMARY KEY,
            name VARCHAR(255),
            applied_on VARCHAR(255),
            checksum VARCHAR(255))";
        if db.execute(fake_refinery_setup, params![]).is_ok() {
            let now = chrono::Local::now();

            // 1|Initial|2021-10-11T23:13:56.840335347-06:00|13384621929958573416
            // 2|Indices|2021-10-11T23:13:56.841740878-06:00|18013925364710952777
            // 3|RemoveFTS|2021-10-11T23:13:56.842433252-06:00|16609115521065592815

            if version > 0 {
                let params = params![1, "Initial", now.to_rfc3339(), "13384621929958573416"];
                db.execute(
                    "INSERT INTO refinery_schema_history VALUES (?, ?, ?, ?)",
                    params,
                )?;
            }
            if version > 1 {
                let params = params![2, "Indices", now.to_rfc3339(), "18013925364710952777"];
                db.execute(
                    "INSERT INTO refinery_schema_history VALUES (?, ?, ?, ?)",
                    params,
                )?;
            }
            if version > 2 {
                let params = params![3, "RemoveFTS", now.to_rfc3339(), "16609115521065592815"];
                db.execute(
                    "INSERT INTO refinery_schema_history VALUES (?, ?, ?, ?)",
                    params,
                )?;
            }
        } else {
            debug!("Refinery migrations already exist for SQLite configuration DB");
        }
    }

    embedded::migrations::runner().run(db).unwrap();
    Ok(())
}

/// Format a DateTime object into the SQLite format.
pub fn format_sqlite_timestamp(dt: &chrono::DateTime<chrono::Utc>) -> String {
    let dt = dt.with_timezone(&chrono::Utc);
    dt.format("%Y-%m-%dT%H:%M:%S.%6f%z").to_string()
}

mod embedded {
    use refinery::embed_migrations;
    embed_migrations!("./resources/sqlite/migrations");
}
<commit_msg>sqlite: typo when opening pool<commit_after>// Copyright (C) 2020 Jason Ish
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use std::path::PathBuf;

use rusqlite::params;
use rusqlite::OpenFlags;

use crate::prelude::*;

pub mod configrepo;
pub mod eventstore;
pub mod importer;
pub mod queryparser;
pub mod retention;

pub async fn open_pool<T: Into<PathBuf>>(filename: T) -> anyhow::Result<deadpool_sqlite::Pool> {
    use deadpool_sqlite::{Config, Runtime};
    let config = Config::new(filename);
    let pool = config.create_pool(Runtime::Tokio1)?;
    let conn = pool.get().await?;
    if let Err(err) = conn
        .interact(|conn| {
            debug!("set journal mode to WAL");
            let mode = conn.pragma_update_and_check(None, "journal_mode", &"WAL", |row| {
                let mode: String = row.get(0)?;
                Ok(mode)
            });
            info!("Result of setting database to WAL mode: {:?}", mode);

            // Set synchronous to NORMAL.
            if let Err(err) = conn.pragma_update(None, "synchronous", &"NORMAL") {
                error!("Failed to set pragma synchronous = NORMAL: {:?}", err);
            }
            match conn.pragma_query_value(None, "synchronous", |row| {
                let val: i32 = row.get(0)?;
                Ok(val)
            }) {
                Ok(mode) => {
                    if mode != 1 {
                        warn!("Database not in synchronous mode normal, instead: {}", mode);
                    }
                }
                Err(err) => {
                    warn!("Failed to query pragma synchronous: {:?}", err);
                }
            }
        })
        .await
    {
        return Err(anyhow::anyhow!("{:?}", err));
    }
    Ok(pool)
}

pub struct ConnectionBuilder {
    pub filename: Option<PathBuf>,
}

impl ConnectionBuilder {
    pub fn filename<T: Into<PathBuf>>(filename: Option<T>) -> ConnectionBuilder {
        ConnectionBuilder {
            filename: filename.map(|filename| filename.into()),
        }
    }

    pub fn open(&self) -> Result<rusqlite::Connection, rusqlite::Error> {
        let flags = OpenFlags::SQLITE_OPEN_READ_WRITE
            | OpenFlags::SQLITE_OPEN_CREATE
            | OpenFlags::SQLITE_OPEN_SHARED_CACHE
            | OpenFlags::SQLITE_OPEN_FULL_MUTEX;
        if let Some(filename) = &self.filename {
            let conn = rusqlite::Connection::open_with_flags(&filename, flags)?;

            // Set WAL mode.
            let mode = conn.pragma_update_and_check(None, "journal_mode", &"WAL", |row| {
                let mode: String = row.get(0)?;
                Ok(mode)
            });
            debug!("Result of setting database to WAL mode: {:?}", mode);

            // Set synchronous to NORMAL.
            if let Err(err) = conn.pragma_update(None, "synchronous", &"NORMAL") {
                error!("Failed to set pragma synchronous = NORMAL: {:?}", err);
            }
            match conn.pragma_query_value(None, "synchronous", |row| {
                let val: i32 = row.get(0)?;
                Ok(val)
            }) {
                Ok(mode) => {
                    if mode != 1 {
                        warn!("Database not in synchronous mode normal, instead: {}", mode);
                    }
                }
                Err(err) => {
                    warn!("Failed to query pragma synchronous: {:?}", err);
                }
            }

            Ok(conn)
        } else {
            rusqlite::Connection::open_in_memory()
        }
    }
}

pub fn init_event_db(db: &mut rusqlite::Connection) -> Result<(), rusqlite::Error> {
    let version = db
        .query_row("select max(version) from schema", params![], |row| {
            let version: i64 = row.get(0).unwrap();
            Ok(version)
        })
        .unwrap_or(-1);
    if version > -1 && version <= 3 {
        // We may have to provide the refinery table, unless it was already created.
        debug!("SQLite configuration DB at v1, checking if setup required for Refinery migrations");
        let fake_refinery_setup = "CREATE TABLE refinery_schema_history(
            version INT4 PRIMARY KEY,
            name VARCHAR(255),
            applied_on VARCHAR(255),
            checksum VARCHAR(255))";
        if db.execute(fake_refinery_setup, params![]).is_ok() {
            let now = chrono::Local::now();

            // 1|Initial|2021-10-11T23:13:56.840335347-06:00|13384621929958573416
            // 2|Indices|2021-10-11T23:13:56.841740878-06:00|18013925364710952777
            // 3|RemoveFTS|2021-10-11T23:13:56.842433252-06:00|16609115521065592815

            if version > 0 {
                let params = params![1, "Initial", now.to_rfc3339(), "13384621929958573416"];
                db.execute(
                    "INSERT INTO refinery_schema_history VALUES (?, ?, ?, ?)",
                    params,
                )?;
            }
            if version > 1 {
                let params = params![2, "Indices", now.to_rfc3339(), "18013925364710952777"];
                db.execute(
                    "INSERT INTO refinery_schema_history VALUES (?, ?, ?, ?)",
                    params,
                )?;
            }
            if version > 2 {
                let params = params![3, "RemoveFTS", now.to_rfc3339(), "16609115521065592815"];
                db.execute(
                    "INSERT INTO refinery_schema_history VALUES (?, ?, ?, ?)",
                    params,
                )?;
            }
        } else {
            debug!("Refinery migrations already exist for SQLite configuration DB");
        }
    }

    embedded::migrations::runner().run(db).unwrap();
    Ok(())
}

/// Format a DateTime object into the SQLite format.
pub fn format_sqlite_timestamp(dt: &chrono::DateTime<chrono::Utc>) -> String {
    let dt = dt.with_timezone(&chrono::Utc);
    dt.format("%Y-%m-%dT%H:%M:%S.%6f%z").to_string()
}

mod embedded {
    use refinery::embed_migrations;
    embed_migrations!("./resources/sqlite/migrations");
}
<|endoftext|>