<commit_before>use std::collections::HashMap;
use std::ops::Deref;

#[derive(Debug, Clone, PartialEq)]
pub enum FieldType {
    Text,
    PlainString,
    I64,
    DateTime,
}

#[derive(Debug, Clone)]
pub struct FieldInfo {
    name: String,
    pub field_type: FieldType,
}

impl FieldInfo {
    pub fn new(name: String, field_type: FieldType) -> FieldInfo {
        FieldInfo {
            name: name,
            field_type: field_type,
        }
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct FieldRef(u32);

#[derive(Debug)]
pub enum AddFieldError {
    FieldAlreadyExists(String),
}

#[derive(Debug, Clone)]
pub struct Schema {
    next_field_id: u32,
    fields: HashMap<FieldRef, FieldInfo>,
    field_names: HashMap<String, FieldRef>,
}

impl Schema {
    pub fn new() -> Schema {
        Schema {
            next_field_id: 1,
            fields: HashMap::new(),
            field_names: HashMap::new(),
        }
    }

    fn new_field_ref(&mut self) -> FieldRef {
        let field_ref = FieldRef(self.next_field_id);
        self.next_field_id += 1;

        field_ref
    }

    pub fn add_field(&mut self, name: String, field_type: FieldType) -> Result<FieldRef, AddFieldError> {
        if self.field_names.contains_key(&name) {
            return Err(AddFieldError::FieldAlreadyExists(name));
        }

        let field_ref = self.new_field_ref();
        let field_info = FieldInfo::new(name.clone(), field_type);

        self.fields.insert(field_ref, field_info);
        self.field_names.insert(name, field_ref);

        Ok(field_ref)
    }

    pub fn get_field_by_name(&self, name: &str) -> Option<FieldRef> {
        self.field_names.get(name).cloned()
    }
}

impl Deref for Schema {
    type Target = HashMap<FieldRef, FieldInfo>;

    fn deref(&self) -> &HashMap<FieldRef, FieldInfo> {
        &self.fields
    }
}
<commit_msg>Added Boolean field type to Schema<commit_after>use std::collections::HashMap;
use std::ops::Deref;

#[derive(Debug, Clone, PartialEq)]
pub enum FieldType {
    Text,
    PlainString,
    I64,
    Boolean,
    DateTime,
}

#[derive(Debug, Clone)]
pub struct FieldInfo {
    name: String,
    pub field_type: FieldType,
}

impl FieldInfo {
    pub fn new(name: String, field_type: FieldType) -> FieldInfo {
        FieldInfo {
            name: name,
            field_type: field_type,
        }
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct FieldRef(u32);

#[derive(Debug)]
pub enum AddFieldError {
    FieldAlreadyExists(String),
}

#[derive(Debug, Clone)]
pub struct Schema {
    next_field_id: u32,
    fields: HashMap<FieldRef, FieldInfo>,
    field_names: HashMap<String, FieldRef>,
}

impl Schema {
    pub fn new() -> Schema {
        Schema {
            next_field_id: 1,
            fields: HashMap::new(),
            field_names: HashMap::new(),
        }
    }

    fn new_field_ref(&mut self) -> FieldRef {
        let field_ref = FieldRef(self.next_field_id);
        self.next_field_id += 1;

        field_ref
    }

    pub fn add_field(&mut self, name: String, field_type: FieldType) -> Result<FieldRef, AddFieldError> {
        if self.field_names.contains_key(&name) {
            return Err(AddFieldError::FieldAlreadyExists(name));
        }

        let field_ref = self.new_field_ref();
        let field_info = FieldInfo::new(name.clone(), field_type);

        self.fields.insert(field_ref, field_info);
        self.field_names.insert(name, field_ref);

        Ok(field_ref)
    }

    pub fn get_field_by_name(&self, name: &str) -> Option<FieldRef> {
        self.field_names.get(name).cloned()
    }
}

impl Deref for Schema {
    type Target = HashMap<FieldRef, FieldInfo>;

    fn deref(&self) -> &HashMap<FieldRef, FieldInfo> {
        &self.fields
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Write;
use std::process;

use applescript::{run as run_applescript};

use plist::Plist;

use tera::{Context, Tera};

use urls;

// http://stackoverflow.com/a/27590832/1558022
macro_rules! error(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

/// Returns true/false if Safari is running.
pub fn is_safari_running() -> bool {
  let cmd_result = process::Command::new("ps")
    .arg("-eaf")
    .output()
    .expect("Unable to test if Safari is running.");
  for line in String::from_utf8(cmd_result.stdout).unwrap().lines() {
    if line.ends_with("Safari.app/Contents/MacOS/Safari") {
      return true
    }
  }
  false
}

/// Exits the program if Safari isn't running.
pub fn assert_safari_is_running() {
  if !is_safari_running() {
    error!("Safari is not running.");
  }
}

/// Return a URL from a Safari window.
///
/// Given a (window, tab) pair, this function looks up the URL of the tab.
/// Note that it doesn't do any error handling, so will throw an execution
/// error if it fails.
///
/// * `window` - Window index.  1 is frontmost.  If None, assumes the
///              frontmost window.
/// * `tab` - Tab index.  1 is leftmost.  If None, assumes the frontmost tab.
///
pub fn get_url(window: Option<i32>, tab: Option<i32>) -> String {
  // If a tab isn't specified, assume the user wants the frontmost tab.
  let command = match window {
    Some(w_idx) => {
      match tab {
        Some(t_idx) => format!("tell application \"Safari\" to get tab {} of window {}", t_idx, w_idx),
        None => format!("tell application \"Safari\" to get URL of document {}", w_idx),
      }
    },
    None => "tell application \"Safari\" to get URL of document 1".to_string(),
  };
  let output = run_applescript(&command);

  if output.status.success() {
    urls::tidy_url(output.stdout.trim())
  } else {
    if output.stderr.contains("Invalid index") {
      error!("Invalid index: no such window or tab.");
    } else {
      error!("Unexpected error from osascript: {:?}", output.stderr);
    }
  }
}

/// Return a list of URLs from Safari.
///
/// This returns a list of URLs, one for every tab in Safari.  Iteration
/// order depends on AppleScript, which I don't think is guaranteed to be
/// stable (in particular, I think it depends on which window is frontmost).
///
pub fn get_all_urls() -> Vec<String> {
  let script = include_str!("scripts/list-open-tabs.scpt");
  let output = run_applescript(&script);
  if output.status.success() {
    output.stdout.trim()
                 .split(", ")
                 .map(|url| urls::tidy_url(url))
                 .filter(|url| url != "favorites://")
                 .collect()
  } else {
    error!("Unexpected error from osascript: {:?}", output.stderr);
  }
}

/// Convert URL patterns into AppleScript conditions.
///
/// These conditions can be used in an `if` statement in AppleScript to
/// decide whether a tab should be closed.  Three patterns are supported,
/// a limited regex syntax:
///
///     example.com             matches anywhere in the URL
///     ^http://examples.com    matches at the start of the URL
///     example.com/$           matches at the end of the URL
///
fn parse_conditions(url_patterns: Vec<&str>) -> Vec<String> {
  url_patterns.iter().map(|p|
    if p.starts_with("^") {
      format!("starts with \"{}\"", p.replace("^", ""))
    } else if p.ends_with("$") {
      format!("ends with \"{}\"", p.replace("$", ""))
    } else {
      format!("contains \"{}\"", p)
    }
  ).collect()
}

/// Tests for parse_conditions().
#[cfg(test)]
mod tests {
  use safari::parse_conditions;

  #[test]
  fn test_parse_conditions() {
    let patterns = vec!["github.com", "^facebook.com", "twitter.com$"];
    let expected = vec!["contains \"github.com\"", "starts with \"facebook.com\"", "ends with \"twitter.com\""];
    let actual = parse_conditions(patterns);
    assert_eq!(actual, expected);
  }
}

/// Close tabs in Safari that match URL patterns.
///
/// Takes a list of URL patterns, and tries to close any matching Safari tabs.
/// Bugginess in AppleScript means this isn't always perfect, but we can
/// have a go.  In general it will fail to close tabs, rather than close
/// the wrong tabs.
///
pub fn close_tabs(url_patterns: Vec<&str>) {
  let conditions = parse_conditions(url_patterns);

  let clean_tabs_template = include_str!("scripts/clean-tabs.scpt");
  let mut context = Context::new();
  context.add("conditions", &conditions);
  let script = Tera::one_off(&clean_tabs_template, context, false).unwrap();

  // Run it twice to get around weird AppleScript bugs.
  run_applescript(&script);
  run_applescript(&script);
}

/// Get the Bookmarks.plist dict for a given title
fn read_bookmarks_plist(title: &str) -> Plist {

  // All Safari data lives at ~/Library/Safari/Bookmarks.plist
  // TODO: There's probably a more idiomatic Rust-like way to write this.
  let mut plist_path = match env::home_dir() {
    Some(v) => v,
    None => error!("Unable to get path to Bookmarks.plist?"),
  };
  plist_path.push("Library/Safari/Bookmarks.plist");

  let file = match File::open(plist_path) {
    Ok(v) => v,
    Err(e) => error!("Unable to open ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let plist = match Plist::read(file) {
    Ok(v) => v,
    Err(e) => error!("Unable to read ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let data = match plist.as_dictionary() {
    Some(v) => v,
    None => error!("Unable to parse ~/Library/Safari/Bookmarks.plist as dictionary?"),
  };

  // The structure of Bookmarks.plist is as follows:
  //
  //     <dict>
  //       <key>Children</key>
  //       <array>
  //         <dict>
  //           <key>Title</key><string>History</string>
  //           ... dict data ...
  //         </dict>
  //         <dict>
  //           <key>Title</key><string>com.apple.ReadingList</string>
  //           ... dict data ...
  //         </dict>
  //         ... other array items ...
  //       </array>
  //     </dict>
  //
  let children = match data.get("Children") {
    Some(child_key) => match child_key.as_array() {
      Some(v) => v,
      None => error!("Top-level children key in ~/Library/Safari/Bookmarks.plist isn't an array?"),
    },
    None => error!("Unable to find top-level Children key in ~/Library/Safari/Bookmarks.plist"),
  };

  let mut matching_children = children.iter().filter(|d|
    match d.as_dictionary() {
      Some(dict) => match dict.get("Title") {
        Some(title_elem) => match title_elem.as_string() {
          Some(v) => (v == title),
          None => false,
        },
        None => false,
      },
      None => false,
    }
  );

  // Check we got one, and only one result.
  let result = match matching_children.next() {
    Some(v) => v,
    None => error!("Unable to find key {} in Bookmarks.plist", title),
  };

  match matching_children.next() {
    Some(_) => error!("Got more than one result for {} in Bookmarks.plist", title),
    None => {},
  };

  result.to_owned()
}

/// Return a list of URLs from Reading List.
///
/// Iteration order depends on the order in which they're stored in
/// Bookmarks.plist, which is usually (but not guaranteed to be) newest first.
///
pub fn get_reading_list_urls() -> Vec<String> {
  let plist = read_bookmarks_plist("com.apple.ReadingList");

  // TODO: All these unwrap() calls should probably be handled better
  let children = plist
    .as_dictionary().unwrap()
    .get("Children").unwrap()
    .as_array().unwrap();

  children
    .iter()
    .map(|child|
      child
        .as_dictionary().unwrap()
        .get("URLString").unwrap()
        .as_string().unwrap()
    )
    .map(|url| urls::tidy_url(url))
    .collect()
}
<commit_msg>Make sure we detect if Safari is running correctly<commit_after>use std::env;
use std::fs::File;
use std::io::Write;
use std::process;

use applescript::{run as run_applescript};

use plist::Plist;

use tera::{Context, Tera};

use urls;

// http://stackoverflow.com/a/27590832/1558022
macro_rules! error(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

/// Returns true/false if Safari is running.
pub fn is_safari_running() -> bool {
  let cmd_result = process::Command::new("ps")
    .arg("-eaf")
    .output()
    .expect("Unable to test if Safari is running.");
  for line in String::from_utf8(cmd_result.stdout).unwrap().lines() {
    if line.contains("Safari.app/Contents/MacOS/Safari") {
      return true
    }
  }
  false
}

/// Exits the program if Safari isn't running.
pub fn assert_safari_is_running() {
  if !is_safari_running() {
    error!("Safari is not running.");
  }
}

/// Return a URL from a Safari window.
///
/// Given a (window, tab) pair, this function looks up the URL of the tab.
/// Note that it doesn't do any error handling, so will throw an execution
/// error if it fails.
///
/// * `window` - Window index.  1 is frontmost.  If None, assumes the
///              frontmost window.
/// * `tab` - Tab index.  1 is leftmost.  If None, assumes the frontmost tab.
///
pub fn get_url(window: Option<i32>, tab: Option<i32>) -> String {
  // If a tab isn't specified, assume the user wants the frontmost tab.
  let command = match window {
    Some(w_idx) => {
      match tab {
        Some(t_idx) => format!("tell application \"Safari\" to get tab {} of window {}", t_idx, w_idx),
        None => format!("tell application \"Safari\" to get URL of document {}", w_idx),
      }
    },
    None => "tell application \"Safari\" to get URL of document 1".to_string(),
  };
  let output = run_applescript(&command);

  if output.status.success() {
    urls::tidy_url(output.stdout.trim())
  } else {
    if output.stderr.contains("Invalid index") {
      error!("Invalid index: no such window or tab.");
    } else {
      error!("Unexpected error from osascript: {:?}", output.stderr);
    }
  }
}

/// Return a list of URLs from Safari.
///
/// This returns a list of URLs, one for every tab in Safari.  Iteration
/// order depends on AppleScript, which I don't think is guaranteed to be
/// stable (in particular, I think it depends on which window is frontmost).
///
pub fn get_all_urls() -> Vec<String> {
  let script = include_str!("scripts/list-open-tabs.scpt");
  let output = run_applescript(&script);
  if output.status.success() {
    output.stdout.trim()
                 .split(", ")
                 .map(|url| urls::tidy_url(url))
                 .filter(|url| url != "favorites://")
                 .collect()
  } else {
    error!("Unexpected error from osascript: {:?}", output.stderr);
  }
}

/// Convert URL patterns into AppleScript conditions.
///
/// These conditions can be used in an `if` statement in AppleScript to
/// decide whether a tab should be closed.  Three patterns are supported,
/// a limited regex syntax:
///
///     example.com             matches anywhere in the URL
///     ^http://examples.com    matches at the start of the URL
///     example.com/$           matches at the end of the URL
///
fn parse_conditions(url_patterns: Vec<&str>) -> Vec<String> {
  url_patterns.iter().map(|p|
    if p.starts_with("^") {
      format!("starts with \"{}\"", p.replace("^", ""))
    } else if p.ends_with("$") {
      format!("ends with \"{}\"", p.replace("$", ""))
    } else {
      format!("contains \"{}\"", p)
    }
  ).collect()
}

/// Tests for parse_conditions().
#[cfg(test)]
mod tests {
  use safari::parse_conditions;

  #[test]
  fn test_parse_conditions() {
    let patterns = vec!["github.com", "^facebook.com", "twitter.com$"];
    let expected = vec!["contains \"github.com\"", "starts with \"facebook.com\"", "ends with \"twitter.com\""];
    let actual = parse_conditions(patterns);
    assert_eq!(actual, expected);
  }
}

/// Close tabs in Safari that match URL patterns.
///
/// Takes a list of URL patterns, and tries to close any matching Safari tabs.
/// Bugginess in AppleScript means this isn't always perfect, but we can
/// have a go.  In general it will fail to close tabs, rather than close
/// the wrong tabs.
///
pub fn close_tabs(url_patterns: Vec<&str>) {
  let conditions = parse_conditions(url_patterns);

  let clean_tabs_template = include_str!("scripts/clean-tabs.scpt");
  let mut context = Context::new();
  context.add("conditions", &conditions);
  let script = Tera::one_off(&clean_tabs_template, context, false).unwrap();

  // Run it twice to get around weird AppleScript bugs.
  run_applescript(&script);
  run_applescript(&script);
}

/// Get the Bookmarks.plist dict for a given title
fn read_bookmarks_plist(title: &str) -> Plist {

  // All Safari data lives at ~/Library/Safari/Bookmarks.plist
  // TODO: There's probably a more idiomatic Rust-like way to write this.
  let mut plist_path = match env::home_dir() {
    Some(v) => v,
    None => error!("Unable to get path to Bookmarks.plist?"),
  };
  plist_path.push("Library/Safari/Bookmarks.plist");

  let file = match File::open(plist_path) {
    Ok(v) => v,
    Err(e) => error!("Unable to open ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let plist = match Plist::read(file) {
    Ok(v) => v,
    Err(e) => error!("Unable to read ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let data = match plist.as_dictionary() {
    Some(v) => v,
    None => error!("Unable to parse ~/Library/Safari/Bookmarks.plist as dictionary?"),
  };

  // The structure of Bookmarks.plist is as follows:
  //
  //     <dict>
  //       <key>Children</key>
  //       <array>
  //         <dict>
  //           <key>Title</key><string>History</string>
  //           ... dict data ...
  //         </dict>
  //         <dict>
  //           <key>Title</key><string>com.apple.ReadingList</string>
  //           ... dict data ...
  //         </dict>
  //         ... other array items ...
  //       </array>
  //     </dict>
  //
  let children = match data.get("Children") {
    Some(child_key) => match child_key.as_array() {
      Some(v) => v,
      None => error!("Top-level children key in ~/Library/Safari/Bookmarks.plist isn't an array?"),
    },
    None => error!("Unable to find top-level Children key in ~/Library/Safari/Bookmarks.plist"),
  };

  let mut matching_children = children.iter().filter(|d|
    match d.as_dictionary() {
      Some(dict) => match dict.get("Title") {
        Some(title_elem) => match title_elem.as_string() {
          Some(v) => (v == title),
          None => false,
        },
        None => false,
      },
      None => false,
    }
  );

  // Check we got one, and only one result.
  let result = match matching_children.next() {
    Some(v) => v,
    None => error!("Unable to find key {} in Bookmarks.plist", title),
  };

  match matching_children.next() {
    Some(_) => error!("Got more than one result for {} in Bookmarks.plist", title),
    None => {},
  };

  result.to_owned()
}

/// Return a list of URLs from Reading List.
///
/// Iteration order depends on the order in which they're stored in
/// Bookmarks.plist, which is usually (but not guaranteed to be) newest first.
///
pub fn get_reading_list_urls() -> Vec<String> {
  let plist = read_bookmarks_plist("com.apple.ReadingList");

  // TODO: All these unwrap() calls should probably be handled better
  let children = plist
    .as_dictionary().unwrap()
    .get("Children").unwrap()
    .as_array().unwrap();

  children
    .iter()
    .map(|child|
      child
        .as_dictionary().unwrap()
        .get("URLString").unwrap()
        .as_string().unwrap()
    )
    .map(|url| urls::tidy_url(url))
    .collect()
}
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
extern crate serialize;
use std::cmp::min;
use std::io::{IoResult, IoError, InvalidInput, SeekStyle, BufWriter, BufReader};
use std::io::{SeekSet, SeekEnd, SeekCur};
use std::mem::size_of;
use sync::{Arc, RWLock};
use serialize::ebml::{reader,writer};
use serialize::{Encodable, Decodable};
mod buffer;

#[deriving(Encodable, Decodable)]
enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

#[deriving(Encodable, Decodable)]
enum SqlAttribute {
	Null,
	NotNull,
}

#[deriving(Encodable, Decodable)]
struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

#[deriving(Encodable, Decodable)]
struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

#[deriving(Encodable, Decodable)]
struct Schema {
	relations: Vec<Relation>,
}

struct SchemaWriter<'a> {
	buffer_manager: &'a mut buffer::BufferManager,
	location: u64,
	maximum: u64,
}

impl<'a> Writer for SchemaWriter<'a> {
	fn write(&mut self, buf: &[u8]) -> IoResult<()> {
		let pageno = self.location / buffer::PAGE_SIZE as u64 + 1;
		let start_from = (self.location % buffer::PAGE_SIZE as u64) as uint;

		let pagelock = self.buffer_manager.fix_page(pageno).unwrap_or_else(|| fail!("Fix page failed"));
		let mut copied = 0;
		{
			let mut page = pagelock.write();
			let content = page.get_mut_data();

			for i in range(0, min(buf.len(), buffer::PAGE_SIZE)) {
				content[start_from+i] = buf[i];
				copied += 1;
				self.location += 1;
			}
		}
		self.buffer_manager.unfix_page(pagelock, true);
		if self.location > self.maximum {
			self.maximum = self.location;
			let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
				|| fail!("Fixing zero page failed"));
			{
				let mut page = pagelock.write();
				let content = page.get_mut_data();
				let mut writer = BufWriter::new(content);
				match writer.write_le_u64(self.maximum) {
					Ok(_) => (),
					Err(e) => fail!("Failed writing lenght to page: {}", e)
				};
			}
			self.buffer_manager.unfix_page(pagelock, true);
		}
		//TODO remaining bytes from buf
		println!("copied {}/{}, location: {}", copied, buf.len(), self.location);
		//assert!(copied, buf.len());
		if copied == buf.len() {
			Ok(())
		} else {
			Err(IoError {
				kind: InvalidInput,
				desc: "Did not copy all data",
				detail: None,
			})
		}
	}
}

impl<'a> Seek for SchemaWriter<'a> {
	fn tell(&self) -> IoResult<u64> {
		Ok(self.location)
	}

	fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
		println!("Seeking {}", pos);
		match style {
			SeekSet => {
				self.location = pos as u64;
				Ok(())
			}
			SeekEnd => {
				Err(IoError {
					kind: InvalidInput,
					desc: "seek to end not supported",
					detail: None,
				})
			}
			SeekCur => {
				self.location = (self.location as i64 + pos) as u64;
				Ok(())
			}
		}
	}
}

impl<'a> SchemaWriter<'a> {
	pub fn new<'b>(bufman: &'b mut buffer::BufferManager) -> SchemaWriter<'b> {
		SchemaWriter {buffer_manager: bufman, location: 0,
			maximum: 0}
	}

	pub fn get_data(&mut self) -> Vec<u8> {
		let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
			|| fail!("Failed fixing 0 page for schema length"));
		let mut size;
		{
			let page = pagelock.read();
			let mut reader = BufReader::new(page.get_data());
			size = reader.read_le_u64().unwrap();
		}
		self.buffer_manager.unfix_page(pagelock, false);
		println!("Size: {}", size);

		let mut data: Vec<u8> = Vec::with_capacity(size as uint);
		let mut read = 0;

		println!("location: {}", self.location);
		for i in range(1, self.location / buffer::PAGE_SIZE as u64 + 2) {
			println!("Reading page {}", i);
			let pagelock = self.buffer_manager.fix_page(i).unwrap_or_else(
				|| fail!("Failed fixing page {}", i));
			{
				let page = pagelock.read();
				let content = page.get_data();
				let mut j = 0;
				while read < size {
					data.push(content[j]);
					j += 1;
					read += 1;
				}
			}
			self.buffer_manager.unfix_page(pagelock, false);
		}
		data
	}
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: &mut buffer::BufferManager) -> Schema {
		let mut wr = SchemaWriter::new(bufmanager);
		let data = wr.get_data();
		let ebml_doc = reader::Doc(data.as_slice());
		let mut deser = reader::Decoder(ebml_doc);
		let value: Schema = match Decodable::decode(&mut deser) {
			Ok(v) => v,
			Err(e) => fail!("Error decoding: {}", e),
		};
		value
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}

	pub fn save_to_disk(&self, bufmanager: &mut buffer::BufferManager) {
		let mut wr = SchemaWriter::new(bufmanager);
		{
			let mut ebml_w = writer::Encoder(&mut wr);
			let _ = self.encode(&mut ebml_w);
		}
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment<'a> {
	id: u64,
	manager: &'a mut buffer::BufferManager,
}

struct SlottedPageHeader {
	slot_count: uint,
	free_slot: uint,
	data_start: uint,
	free_space: uint,
}

struct Slot(uint, uint);

struct SlottedPage {
	header: SlottedPageHeader,
	frame: Arc<RWLock<buffer::BufferFrame>>,
}

impl SlottedPage {
	pub fn new(frame: Arc<RWLock<buffer::BufferFrame>>) -> SlottedPage {
		let header = {
			let mut frame = frame.write();
			let mut br = BufReader::new(frame.get_data());
			let slot_count = br.read_le_uint().unwrap();
			let free_slot = br.read_le_uint().unwrap();
			let data_start = br.read_le_uint().unwrap();
			let free_space = br.read_le_uint().unwrap();

			if slot_count == 0 && free_space == 0 {
				// blank frame, construct header
				let slot_count = 0;
				let free_slot = 0;
				let data_start = buffer::PAGE_SIZE;
				let free_space = buffer::PAGE_SIZE - size_of::<SlottedPageHeader>();
				SlottedPageHeader {slot_count: slot_count,
					free_slot: free_slot, data_start: data_start,
					free_space: free_space}
			} else {
				SlottedPageHeader {slot_count: slot_count,
					free_slot: free_slot, data_start: data_start,
					free_space: free_space}
			}

		};
		println!("SlottedPageHeader: {:?}", header);
		SlottedPage {frame: frame, header: header}
	}

	fn write_header(&mut self) {
		let mut frame = self.frame.write();
		let mut bw = BufWriter::new(frame.get_mut_data());
		bw.write_le_uint(self.header.slot_count);
		bw.write_le_uint(self.header.free_slot);
		bw.write_le_uint(self.header.data_start);
		bw.write_le_uint(self.header.free_space);
	}

	fn try_insert(&mut self, r: &Record) -> (bool, uint) {
		println!("s.h.free_space {}", self.header.free_space);
		if self.header.free_space < r.len {
			return (false, 0)
		}
		// adjust the new start of data to be more to the frone
		self.header.data_start -= r.len;
		// we added the data plus one slot, reduce free space
		self.header.free_space -= r.len + size_of::<Slot>();
		let slot = Slot(self.header.data_start, r.len);
		{
			let mut frame = self.frame.write();
			let mut bw = BufWriter::new(frame.get_mut_data());
			// seek to place where we can store data
			bw.seek(self.header.data_start as i64, SeekSet);
			// copy it over from record
			bw.write(r.get_data());
			// seek to beginning of slot storage
			bw.seek((size_of::<SlottedPageHeader>() +
				self.header.slot_count * size_of::<Slot>()) as i64,
				SeekSet);
			// write out slot
			let Slot(offset, len) = slot;
			bw.write_le_uint(offset);
			bw.write_le_uint(len);
		}
		let res = (true, self.header.slot_count);
		self.header.free_slot += 1;
		self.header.slot_count += 1;

		self.write_header();
		res
	}
}

struct TID {
	page_id: u64,
	slot_id: uint,
}

fn join_segment(segment: u64, page: u64) -> u64{
	(segment << buffer::PAGE_BITS) | page
}

impl<'a> SPSegment<'a> {
	pub fn insert(&mut self, r: &Record) -> TID {
		let mut tid = TID {page_id: 0, slot_id: 0};
		println!("inserting")
		for i in range(0, 1<<buffer::PAGE_BITS) {
			println!("Testing {}", i);
			let pagelock = match self.manager.fix_page(join_segment(self.id, i as u64)) {
				Some(p) => p,
				None => fail!("Failed aquiring page {}", i),
			};
			let mut sp = SlottedPage::new(pagelock.clone());
			let (inserted, slot) = sp.try_insert(r);
			println!("try_insert: {}", inserted);
			self.manager.unfix_page(pagelock, inserted);
			if inserted {
				tid = TID {page_id: i as u64, slot_id: slot};
				break;
			}
			break;
		}
		tid
	}

	pub fn remove(tid: TID) -> bool {
		false
	}

	pub fn lookup(tid: TID) -> Record {
		// TODO
		Record {len: 1, data: vec!(1)}
	}

	pub fn update(tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);

	let mut manager = buffer::BufferManager::new(1024, Path::new("."));
	schema.save_to_disk(&mut manager);
	let new_schema = Schema::new_from_disk(&mut manager);
	println!("new_schema == {:?}", new_schema);
	assert!(false);
}

#[test]
fn slotted_page_create() {
	let mut manager = buffer::BufferManager::new(1024, Path::new("."));
	let mut seg = SPSegment {id: 1, manager: &mut manager};
	let rec = Record {len: 1, data: vec!(42)};
	let tid = seg.insert(&rec);
	println!("TID: {:?}", tid);
	assert!(false);
}
<commit_msg>Implemented record lookup<commit_after>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
extern crate serialize;
use std::cmp::min;
use std::io::{IoResult, IoError, InvalidInput, SeekStyle, BufWriter, BufReader};
use std::io::{SeekSet, SeekEnd, SeekCur};
use std::mem::size_of;
use sync::{Arc, RWLock};
use serialize::ebml::{reader,writer};
use serialize::{Encodable, Decodable};
mod buffer;

#[deriving(Encodable, Decodable)]
enum SqlType {
	Char(uint),
	Varchar(uint),
	Integer,
}

#[deriving(Encodable, Decodable)]
enum SqlAttribute {
	Null,
	NotNull,
}

#[deriving(Encodable, Decodable)]
struct Column {
	name: ~str,
	datatype: SqlType,
	attributes: Vec<SqlAttribute>,
}

#[deriving(Encodable, Decodable)]
struct Relation {
	name: ~str,
	columns: Vec<Column>,
}

impl Relation {
	pub fn new(name: ~str) -> Relation {
		Relation {name: name, columns: Vec::new()}
	}

	pub fn add_column(&mut self, column: Column) {
		self.columns.push(column);
	}
}

#[deriving(Encodable, Decodable)]
struct Schema {
	relations: Vec<Relation>,
}

struct SchemaWriter<'a> {
	buffer_manager: &'a mut buffer::BufferManager,
	location: u64,
	maximum: u64,
}

impl<'a> Writer for SchemaWriter<'a> {
	fn write(&mut self, buf: &[u8]) -> IoResult<()> {
		let pageno = self.location / buffer::PAGE_SIZE as u64 + 1;
		let start_from = (self.location % buffer::PAGE_SIZE as u64) as uint;

		let pagelock = self.buffer_manager.fix_page(pageno).unwrap_or_else(|| fail!("Fix page failed"));
		let mut copied = 0;
		{
			let mut page = pagelock.write();
			let content = page.get_mut_data();

			for i in range(0, min(buf.len(), buffer::PAGE_SIZE)) {
				content[start_from+i] = buf[i];
				copied += 1;
				self.location += 1;
			}
		}
		self.buffer_manager.unfix_page(pagelock, true);
		if self.location > self.maximum {
			self.maximum = self.location;
			let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
				|| fail!("Fixing zero page failed"));
			{
				let mut page = pagelock.write();
				let content = page.get_mut_data();
				let mut writer = BufWriter::new(content);
				match writer.write_le_u64(self.maximum) {
					Ok(_) => (),
					Err(e) => fail!("Failed writing lenght to page: {}", e)
				};
			}
			self.buffer_manager.unfix_page(pagelock, true);
		}
		//TODO remaining bytes from buf
		println!("copied {}/{}, location: {}", copied, buf.len(), self.location);
		//assert!(copied, buf.len());
		if copied == buf.len() {
			Ok(())
		} else {
			Err(IoError {
				kind: InvalidInput,
				desc: "Did not copy all data",
				detail: None,
			})
		}
	}
}

impl<'a> Seek for SchemaWriter<'a> {
	fn tell(&self) -> IoResult<u64> {
		Ok(self.location)
	}

	fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
		println!("Seeking {}", pos);
		match style {
			SeekSet => {
				self.location = pos as u64;
				Ok(())
			}
			SeekEnd => {
				Err(IoError {
					kind: InvalidInput,
					desc: "seek to end not supported",
					detail: None,
				})
			}
			SeekCur => {
				self.location = (self.location as i64 + pos) as u64;
				Ok(())
			}
		}
	}
}

impl<'a> SchemaWriter<'a> {
	pub fn new<'b>(bufman: &'b mut buffer::BufferManager) -> SchemaWriter<'b> {
		SchemaWriter {buffer_manager: bufman, location: 0,
			maximum: 0}
	}

	pub fn get_data(&mut self) -> Vec<u8> {
		let pagelock = self.buffer_manager.fix_page(0).unwrap_or_else(
			|| fail!("Failed fixing 0 page for schema length"));
		let mut size;
		{
			let page = pagelock.read();
			let mut reader = BufReader::new(page.get_data());
			size = reader.read_le_u64().unwrap();
		}
		self.buffer_manager.unfix_page(pagelock, false);
		println!("Size: {}", size);

		let mut data: Vec<u8> = Vec::with_capacity(size as uint);
		let mut read = 0;

		println!("location: {}", self.location);
		for i in range(1, self.location / buffer::PAGE_SIZE as u64 + 2) {
			println!("Reading page {}", i);
			let pagelock = self.buffer_manager.fix_page(i).unwrap_or_else(
				|| fail!("Failed fixing page {}", i));
			{
				let page = pagelock.read();
				let content = page.get_data();
				let mut j = 0;
				while read < size {
					data.push(content[j]);
					j += 1;
					read += 1;
				}
			}
			self.buffer_manager.unfix_page(pagelock, false);
		}
		data
	}
}

impl Schema {
	pub fn new() -> Schema {
		Schema {relations: Vec::new()}
	}

	pub fn new_from_disk(bufmanager: &mut buffer::BufferManager) -> Schema {
		let mut wr = SchemaWriter::new(bufmanager);
		let data = wr.get_data();
		let ebml_doc = reader::Doc(data.as_slice());
		let mut deser = reader::Decoder(ebml_doc);
		let value: Schema = match Decodable::decode(&mut deser) {
			Ok(v) => v,
			Err(e) => fail!("Error decoding: {}", e),
		};
		value
	}

	pub fn add_relation(&mut self, relation: Relation) {
		self.relations.push(relation);
	}

	pub fn save_to_disk(&self, bufmanager: &mut buffer::BufferManager) {
		let mut wr = SchemaWriter::new(bufmanager);
		{
			let mut ebml_w = writer::Encoder(&mut wr);
			let _ = self.encode(&mut ebml_w);
		}
	}
}

struct Record {
	// TODO: do we even need this?
	len: uint,
	data: Vec<u8>,
}

impl Record {
	/* move semantics, no copy */
	pub fn new(len: uint, data: Vec<u8>) -> Record {
		Record {len: len, data: data}
	}

	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

struct SPSegment<'a> {
	id: u64,
	manager: &'a mut buffer::BufferManager,
}

struct SlottedPageHeader {
	slot_count: uint,
	free_slot: uint,
	data_start: uint,
	free_space: uint,
}

struct Slot(uint, uint);

struct SlottedPage {
	header: SlottedPageHeader,
	frame: Arc<RWLock<buffer::BufferFrame>>,
}

impl SlottedPage {
	pub fn new(frame: Arc<RWLock<buffer::BufferFrame>>) -> SlottedPage {
		let header = {
			let mut frame = frame.write();
			let mut br = BufReader::new(frame.get_data());
			let slot_count = br.read_le_uint().unwrap();
			let free_slot = br.read_le_uint().unwrap();
			let data_start = br.read_le_uint().unwrap();
			let free_space = br.read_le_uint().unwrap();

			if slot_count == 0 && free_space == 0 {
				// blank frame, construct header
				let slot_count = 0;
				let free_slot = 0;
				let data_start = buffer::PAGE_SIZE;
				let free_space = buffer::PAGE_SIZE - size_of::<SlottedPageHeader>();
				SlottedPageHeader {slot_count: slot_count,
					free_slot: free_slot, data_start: data_start,
					free_space: free_space}
			} else {
				SlottedPageHeader {slot_count: slot_count,
					free_slot: free_slot, data_start: data_start,
					free_space: free_space}
			}

		};
		println!("SlottedPageHeader: {:?}", header);
		SlottedPage {frame: frame, header: header}
	}

	fn write_header(&mut self) {
		let mut frame = self.frame.write();
		let mut bw = BufWriter::new(frame.get_mut_data());
		bw.write_le_uint(self.header.slot_count);
		bw.write_le_uint(self.header.free_slot);
		bw.write_le_uint(self.header.data_start);
		bw.write_le_uint(self.header.free_space);
	}

	fn try_insert(&mut self, r: &Record) -> (bool, uint) {
		println!("s.h.free_space {}", self.header.free_space);
		if self.header.free_space < r.len {
			return (false, 0)
		}
		// adjust the new start of data to be more to the frone
		self.header.data_start -= r.len;
		// we added the data plus one slot, reduce free space
		self.header.free_space -= r.len + size_of::<Slot>();
		let slot = Slot(self.header.data_start, r.len);
		{
			let mut frame = self.frame.write();
			let mut bw = BufWriter::new(frame.get_mut_data());
			// seek to place where we can store data
			bw.seek(self.header.data_start as i64, SeekSet);
			// copy it over from record
			bw.write(r.get_data());
			// seek to beginning of slot storage
			bw.seek((size_of::<SlottedPageHeader>() +
				self.header.slot_count * size_of::<Slot>()) as i64,
				SeekSet);
			// write out slot
			let Slot(offset, len) = slot;
			bw.write_le_uint(offset);
			bw.write_le_uint(len);
		}
		let res = (true, self.header.slot_count);
		self.header.free_slot += 1;
		self.header.slot_count += 1;

		self.write_header();
		res
	}

	fn lookup(&self, slot_id: uint) -> Record {
		let frame = self.frame.read();
		let mut br = BufReader::new(frame.get_data());
		// move to slot position
		br.seek((size_of::<SlottedPageHeader>() + slot_id * size_of::<Slot>()) as i64,
			SeekSet);
		// read offset and length of slot_id
		let offset = br.read_le_uint().unwrap();
		let len = br.read_le_uint().unwrap();
		// jump to that offset
		br.seek(offset as i64, SeekSet);
		// read length of data from there
		let content = match br.read_exact(len) {
			Ok(c) => c,
			Err(e) => fail!("Failed reading from segmented page, {}", e),
		};
		// construct and return a record from that data
		let v = Vec::from_slice(content);
		Record {len: len, data: v}
	}
}

struct TID {
	page_id: u64,
	slot_id: uint,
}

fn join_segment(segment: u64, page: u64) -> u64{
	(segment << buffer::PAGE_BITS) | page
}

impl<'a> SPSegment<'a> {
	pub fn insert(&mut self, r: &Record) -> TID {
		let mut tid = TID {page_id: 0, slot_id: 0};
		println!("inserting")
		for i in range(0, 1<<buffer::PAGE_BITS) {
			println!("Testing {}", i);
			let pagelock = match self.manager.fix_page(join_segment(self.id, i as u64)) {
				Some(p) => p,
				None => fail!("Failed aquiring page {}", i),
			};
			let mut sp = SlottedPage::new(pagelock.clone());
			let (inserted, slot) = sp.try_insert(r);
			println!("try_insert: {}", inserted);
			self.manager.unfix_page(pagelock, inserted);
			if inserted {
				tid = TID {page_id: i as u64, slot_id: slot};
				break;
			}
			break;
		}
		tid
	}

	pub fn remove(&self, tid: TID) -> bool {
		false
	}

	pub fn lookup(&mut self, tid: TID) -> Record {
		let page_id = tid.page_id;
		let slot_id = tid.slot_id;
		let pagelock = match self.manager.fix_page(join_segment(self.id, page_id)) {
			Some(p) => p,
			None => fail!("Failed looking up page {}", page_id),
		};
		let sp = SlottedPage::new(pagelock.clone());
		sp.lookup(slot_id)
	}

	pub fn update(&self, tid: TID, r: Record) -> bool {
		false
	}
}

#[test]
fn main() {
}

#[test]
fn create_schema() {
	let name = Column {name: ~"name", datatype: Varchar(128), attributes: vec!(NotNull)};
	let age = Column {name: ~"age", datatype: Integer, attributes: vec!(NotNull)};
	let mut relation = Relation::new(~"Person");
	relation.add_column(name);
	relation.add_column(age);
	let mut schema = Schema::new();
	schema.add_relation(relation);

	let mut manager = buffer::BufferManager::new(1024, Path::new("."));
	schema.save_to_disk(&mut manager);
	let new_schema = Schema::new_from_disk(&mut manager);
	println!("new_schema == {:?}", new_schema);
	assert!(false);
}

#[test]
fn slotted_page_create() {
	let mut manager = buffer::BufferManager::new(1024, Path::new("."));
	let mut seg = SPSegment {id: 1, manager: &mut manager};
	let rec = Record {len: 1, data: vec!(42)};
	let tid = seg.insert(&rec);
	println!("TID: {:?}", tid);
	let rec2 = seg.lookup(tid);
	println!("Record: {}", rec2.data);
	assert!(false);
}
<|endoftext|>


<commit_before>use std::collections::{HashMap, HashSet};
use std::time::Duration;

use crate::controls::Control;
use crate::ldap::Ldap;
use crate::parse_filter;
use crate::protocol::LdapOp;
use crate::result::{LdapError, LdapResult, Result};

use tokio::sync::mpsc;
use tokio::time;

use lber::common::TagClass;
use lber::parse::parse_tag;
use lber::structure::{StructureTag, PL};
use lber::structures::{Boolean, Enumerated, Integer, OctetString, Sequence, Tag};
use lber::universal::Types;
use lber::IResult;

/// Possible values for search scope.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Scope {
    /// Base object; search only the object named in the base DN.
    Base = 0,
    /// Search the objects immediately below the base DN.
    OneLevel = 1,
    /// Search the object named in the base DN and the whole subtree below it.
    Subtree = 2,
}

/// Possible values for alias dereferencing during search.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DerefAliases {
    /// Never dereference.
    Never = 0,
    /// Dereference while retrieving objects according to search scope.
    Searching = 1,
    /// Dereference while finding the base object.
    Finding = 2,
    /// Always dereference.
    Always = 3,
}

#[derive(Debug)]
pub enum SearchItem {
    Entry(StructureTag),
    Referral(StructureTag),
    Done(LdapResult, Vec<Control>),
}

/// Wrapper for the internal structure of a result entry.
#[derive(Debug, Clone)]
pub struct ResultEntry(pub(crate) StructureTag);

impl ResultEntry {
    #[doc(hidden)]
    pub fn new(st: StructureTag) -> ResultEntry {
        ResultEntry(st)
    }

    pub fn is_ref(&self) -> bool {
        self.0.id == 19
    }

    pub fn is_intermediate(&self) -> bool {
        self.0.id == 25
    }
}

/// Additional parameters for the Search operation.
#[derive(Clone, Debug)]
pub struct SearchOptions {
    pub(crate) deref: DerefAliases,
    pub(crate) typesonly: bool,
    pub(crate) timelimit: i32,
    pub(crate) sizelimit: i32,
    pub(crate) autopage: Option<i32>,
}

impl SearchOptions {
    /// Create an instance of the structure with default values.
    pub fn new() -> Self {
        SearchOptions {
            deref: DerefAliases::Never,
            typesonly: false,
            timelimit: 0,
            sizelimit: 0,
            autopage: None,
        }
    }

    /// Set the method for dereferencing aliases.
    pub fn deref(mut self, d: DerefAliases) -> Self {
        self.deref = d;
        self
    }

    /// Set the indicator of returning just attribute names (`true`) vs. names and values (`false`).
    pub fn typesonly(mut self, typesonly: bool) -> Self {
        self.typesonly = typesonly;
        self
    }

    /// Set the time limit, in seconds, for the whole search operation.
    ///
    /// This is a server-side limit of the elapsed time for performing the operation, _not_ a
    /// network timeout for retrieving result entries or the result of the whole operation.
    pub fn timelimit(mut self, timelimit: i32) -> Self {
        self.timelimit = timelimit;
        self
    }

    /// Set the size limit, in entries, for the whole search operation.
    pub fn sizelimit(mut self, sizelimit: i32) -> Self {
        self.sizelimit = sizelimit;
        self
    }

    /// Set the page size for automatic PagedResults.
    ///
    /// If `pagesize` is greater than zero, use a PagedResults control with that
    /// page size for the next search request, and keep issuing requests until
    /// all results are returned, or an error or a timeout occur.
    ///
    /// Supplying another PagedResults control to the initial request is not allowed,
    /// and will generate an error. Other controls may be specified, and are replicated
    /// afresh in every subsequent search request. Care should be taken not to depend
    /// on response controls, because intermediate search results are not returned
    /// to the caller.
    ///
    /// Passing a `pagesize` less than or equal to zero will turn autopaging off.
    pub fn autopage(mut self, pagesize: i32) -> Self {
        self.autopage = Some(pagesize);
        self
    }
}

/// Parsed search result entry.
///
/// While LDAP attributes can have a variety of syntaxes, they're all returned in
/// search results as octet strings, without any associated type information. A
/// general-purpose result parser could leave all values in that format, but then
/// retrieving them from user code would be cumbersome and tedious.
///
/// For that reason, the parser tries to convert every value into a `String`. If an
/// attribute can contain unconstrained binary strings, the conversion may fail. In that case,
/// the attribute and all its values will be in the `bin_attrs` hashmap. Since it's
/// possible that a particular set of values for a binary attribute _could_ be
/// converted into UTF-8 `String`s, the presence of of such attribute in the result
/// entry should be checked for both in `attrs` and `bin_atrrs`.
///
/// In the future versions of the library, this parsing interface will be
/// de-emphasized in favor of custom Serde deserialization of search results directly
/// into a user-supplied struct, which is expected to be a better fit for the
/// majority of uses.
#[derive(Debug, Clone)]
pub struct SearchEntry {
    /// Entry DN.
    pub dn: String,
    /// Attributes.
    pub attrs: HashMap<String, Vec<String>>,
    /// Binary-valued attributes.
    pub bin_attrs: HashMap<String, Vec<Vec<u8>>>,
}

impl SearchEntry {
    /// Parse raw BER data and convert it into attribute map(s).
    ///
    /// __Note__: this function will panic on parsing error. Error handling will be
    /// improved in a future version of the library.
    pub fn construct(re: ResultEntry) -> SearchEntry {
        let mut tags =
            re.0.match_id(4)
                .and_then(|t| t.expect_constructed())
                .expect("entry")
                .into_iter();
        let dn = String::from_utf8(
            tags.next()
                .expect("element")
                .expect_primitive()
                .expect("octet string"),
        )
        .expect("dn");
        let mut attr_vals = HashMap::new();
        let mut bin_attr_vals = HashMap::new();
        let attrs = tags
            .next()
            .expect("element")
            .expect_constructed()
            .expect("attrs")
            .into_iter();
        for a_v in attrs {
            let mut part_attr = a_v
                .expect_constructed()
                .expect("partial attribute")
                .into_iter();
            let a_type = String::from_utf8(
                part_attr
                    .next()
                    .expect("element")
                    .expect_primitive()
                    .expect("octet string"),
            )
            .expect("attribute type");
            let mut any_binary = false;
            let values = part_attr
                .next()
                .expect("element")
                .expect_constructed()
                .expect("values")
                .into_iter()
                .map(|t| t.expect_primitive().expect("octet string"))
                .filter_map(|s| {
                    if let Ok(s) = std::str::from_utf8(s.as_ref()) {
                        return Some(s.to_owned());
                    }
                    bin_attr_vals
                        .entry(a_type.clone())
                        .or_insert_with(|| vec![])
                        .push(s);
                    any_binary = true;
                    None
                })
                .collect::<Vec<String>>();
            if any_binary {
                bin_attr_vals.get_mut(&a_type).expect("bin vector").extend(
                    values
                        .into_iter()
                        .map(String::into_bytes)
                        .collect::<Vec<Vec<u8>>>(),
                );
            } else {
                attr_vals.insert(a_type, values);
            }
        }
        SearchEntry {
            dn: dn,
            attrs: attr_vals,
            bin_attrs: bin_attr_vals,
        }
    }
}

#[derive(Debug)]
pub struct SearchStream {
    ldap: Ldap,
    rx: Option<mpsc::UnboundedReceiver<SearchItem>>,
    req: Option<Tag>,
    timeout: Option<Duration>,
    res: Option<LdapResult>,
}

impl SearchStream {
    pub(crate) fn new(ldap: Ldap) -> Self {
        SearchStream {
            ldap,
            rx: None,
            req: None,
            timeout: None,
            res: None,
        }
    }

    /// See also [`LdapConn::streaming_search()`](struct.LdapConn.html#method.streaming_search).
    ///
    /// The returned future resolves to a [`SearchStream`](struct.SearchStream.html),
    /// which should be iterated through to obtain results. Before starting the iteration,
    /// the receiver future, which will yield the overall result of the search after the stream
    /// is drained, should be retrieved from the stream instance with
    /// [`get_result_rx()`](struct.SearchStream.html#method.get_result_rx). The stream and
    /// the receiver should be polled concurrently with `Future::join()`.
    pub(crate) async fn start<S: AsRef<str>>(
        mut self,
        base: &str,
        scope: Scope,
        filter: &str,
        attrs: Vec<S>,
    ) -> Result<Self> {
        let opts = match self.ldap.search_opts.take() {
            Some(opts) => opts,
            None => SearchOptions::new(),
        };
        self.timeout = self.ldap.timeout.take();
        let req = Tag::Sequence(Sequence {
            id: 3,
            class: TagClass::Application,
            inner: vec![
                Tag::OctetString(OctetString {
                    inner: Vec::from(base.as_bytes()),
                    ..Default::default()
                }),
                Tag::Enumerated(Enumerated {
                    inner: scope as i64,
                    ..Default::default()
                }),
                Tag::Enumerated(Enumerated {
                    inner: opts.deref as i64,
                    ..Default::default()
                }),
                Tag::Integer(Integer {
                    inner: opts.sizelimit as i64,
                    ..Default::default()
                }),
                Tag::Integer(Integer {
                    inner: opts.timelimit as i64,
                    ..Default::default()
                }),
                Tag::Boolean(Boolean {
                    inner: opts.typesonly,
                    ..Default::default()
                }),
                match parse_filter(filter) {
                    Ok(filter) => filter,
                    _ => return Err(LdapError::FilterParsing),
                },
                Tag::Sequence(Sequence {
                    inner: attrs
                        .into_iter()
                        .map(|s| {
                            Tag::OctetString(OctetString {
                                inner: Vec::from(s.as_ref()),
                                ..Default::default()
                            })
                        })
                        .collect(),
                    ..Default::default()
                }),
            ],
        });
        let (tx, rx) = mpsc::unbounded_channel();
        self.rx = Some(rx);
        if let Some(ref timeout) = self.timeout {
            self.ldap.with_timeout(*timeout);
        }
        self.ldap.op_call(LdapOp::Search(tx), req).await?;
        Ok(self)
    }

    pub async fn next(&mut self) -> Result<Option<ResultEntry>> {
        if self.rx.is_none() {
            return Ok(None);
        }
        let item = if let Some(ref timeout) = self.timeout {
            time::timeout(*timeout, self.rx.as_mut().unwrap().recv()).await?
        } else {
            self.rx.as_mut().unwrap().recv().await
        };
        let item = match item {
            Some(item) => item,
            None => {
                self.rx = None;
                return Err(LdapError::EndOfStream);
            }
        };
        match item {
            SearchItem::Entry(tag) | SearchItem::Referral(tag) => {
                return Ok(Some(ResultEntry(tag)))
            }
            SearchItem::Done(mut res, controls) => {
                res.ctrls = controls;
                self.res = Some(res);
                self.rx = None;
            }
        }
        Ok(None)
    }

    pub fn finish(mut self) -> LdapResult {
        self.rx = None;
        self.res.unwrap_or_else(|| LdapResult {
            rc: 88,
            matched: String::from(""),
            text: String::from("user cancelled"),
            refs: vec![],
            ctrls: vec![],
        })
    }
}

#[derive(Clone, Debug)]
pub enum SyncInfo {
    NewCookie(Vec<u8>),
    RefreshDelete {
        cookie: Option<Vec<u8>>,
        refresh_done: bool,
    },
    RefreshPresent {
        cookie: Option<Vec<u8>>,
        refresh_done: bool,
    },
    SyncIdSet {
        cookie: Option<Vec<u8>>,
        refresh_deletes: bool,
        sync_uuids: HashSet<Vec<u8>>,
    },
}

pub fn parse_syncinfo<V: AsRef<[u8]>>(raw: V) -> SyncInfo {
    let syncinfo_val = match parse_tag(raw.as_ref()) {
        IResult::Done(_, tag) => tag,
        _ => panic!("syncinfo_val: not a tag"),
    };
    match syncinfo_val {
        StructureTag { id, class, payload } if class == TagClass::Context && id < 4 => match id {
            0 => {
                let cookie = match payload {
                    PL::P(payload) => payload,
                    PL::C(_) => panic!(),
                };
                SyncInfo::NewCookie(cookie)
            }
            1 | 2 | 3 => {
                let mut syncinfo_val = match payload {
                    PL::C(payload) => payload,
                    PL::P(_) => panic!(),
                }
                .into_iter();
                let mut sync_cookie = None;
                let mut flag = id != 3;
                let mut uuids = HashSet::new();
                let mut pass = 1;
                'it: loop {
                    match syncinfo_val.next() {
                        None => break 'it,
                        Some(comp) => match comp {
                            StructureTag {
                                id,
                                class,
                                payload: _,
                            } if class == TagClass::Universal
                                && id == Types::OctetString as u64
                                && pass <= 1 =>
                            {
                                sync_cookie = comp.expect_primitive();
                            }
                            StructureTag {
                                id,
                                class,
                                payload: _,
                            } if class == TagClass::Universal
                                && id == Types::Boolean as u64
                                && pass <= 2 =>
                            {
                                flag = !(comp.expect_primitive().expect("octet string")[0] == 0);
                            }
                            StructureTag {
                                id,
                                class,
                                payload: _,
                            } if class == TagClass::Universal
                                && id == Types::Set as u64
                                && pass <= 3 =>
                            {
                                uuids = comp
                                    .expect_constructed()
                                    .expect("uuid set")
                                    .into_iter()
                                    .map(|u| u.expect_primitive().expect("octet string"))
                                    .collect();
                            }
                            _ => panic!(),
                        },
                    }
                    pass += 1;
                }
                match id {
                    1 => SyncInfo::RefreshDelete {
                        cookie: sync_cookie,
                        refresh_done: flag,
                    },
                    2 => SyncInfo::RefreshPresent {
                        cookie: sync_cookie,
                        refresh_done: flag,
                    },
                    3 => SyncInfo::SyncIdSet {
                        cookie: sync_cookie,
                        refresh_deletes: flag,
                        sync_uuids: uuids,
                    },
                    _ => panic!(),
                }
            }
            _ => panic!(),
        },
        _ => panic!("syncinfo id not 0-3"),
    }
}

pub fn parse_refs(t: StructureTag) -> Vec<String> {
    t.expect_constructed()
        .expect("referrals")
        .into_iter()
        .map(|t| t.expect_primitive().expect("octet string"))
        .map(String::from_utf8)
        .map(|s| s.expect("uri"))
        .collect()
}
<commit_msg>Remove autopage setting<commit_after>use std::collections::{HashMap, HashSet};
use std::time::Duration;

use crate::controls::Control;
use crate::ldap::Ldap;
use crate::parse_filter;
use crate::protocol::LdapOp;
use crate::result::{LdapError, LdapResult, Result};

use tokio::sync::mpsc;
use tokio::time;

use lber::common::TagClass;
use lber::parse::parse_tag;
use lber::structure::{StructureTag, PL};
use lber::structures::{Boolean, Enumerated, Integer, OctetString, Sequence, Tag};
use lber::universal::Types;
use lber::IResult;

/// Possible values for search scope.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Scope {
    /// Base object; search only the object named in the base DN.
    Base = 0,
    /// Search the objects immediately below the base DN.
    OneLevel = 1,
    /// Search the object named in the base DN and the whole subtree below it.
    Subtree = 2,
}

/// Possible values for alias dereferencing during search.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum DerefAliases {
    /// Never dereference.
    Never = 0,
    /// Dereference while retrieving objects according to search scope.
    Searching = 1,
    /// Dereference while finding the base object.
    Finding = 2,
    /// Always dereference.
    Always = 3,
}

#[derive(Debug)]
pub enum SearchItem {
    Entry(StructureTag),
    Referral(StructureTag),
    Done(LdapResult, Vec<Control>),
}

/// Wrapper for the internal structure of a result entry.
#[derive(Debug, Clone)]
pub struct ResultEntry(pub(crate) StructureTag);

impl ResultEntry {
    #[doc(hidden)]
    pub fn new(st: StructureTag) -> ResultEntry {
        ResultEntry(st)
    }

    pub fn is_ref(&self) -> bool {
        self.0.id == 19
    }

    pub fn is_intermediate(&self) -> bool {
        self.0.id == 25
    }
}

/// Additional parameters for the Search operation.
#[derive(Clone, Debug)]
pub struct SearchOptions {
    pub(crate) deref: DerefAliases,
    pub(crate) typesonly: bool,
    pub(crate) timelimit: i32,
    pub(crate) sizelimit: i32,
}

impl SearchOptions {
    /// Create an instance of the structure with default values.
    pub fn new() -> Self {
        SearchOptions {
            deref: DerefAliases::Never,
            typesonly: false,
            timelimit: 0,
            sizelimit: 0,
        }
    }

    /// Set the method for dereferencing aliases.
    pub fn deref(mut self, d: DerefAliases) -> Self {
        self.deref = d;
        self
    }

    /// Set the indicator of returning just attribute names (`true`) vs. names and values (`false`).
    pub fn typesonly(mut self, typesonly: bool) -> Self {
        self.typesonly = typesonly;
        self
    }

    /// Set the time limit, in seconds, for the whole search operation.
    ///
    /// This is a server-side limit of the elapsed time for performing the operation, _not_ a
    /// network timeout for retrieving result entries or the result of the whole operation.
    pub fn timelimit(mut self, timelimit: i32) -> Self {
        self.timelimit = timelimit;
        self
    }

    /// Set the size limit, in entries, for the whole search operation.
    pub fn sizelimit(mut self, sizelimit: i32) -> Self {
        self.sizelimit = sizelimit;
        self
    }
}

/// Parsed search result entry.
///
/// While LDAP attributes can have a variety of syntaxes, they're all returned in
/// search results as octet strings, without any associated type information. A
/// general-purpose result parser could leave all values in that format, but then
/// retrieving them from user code would be cumbersome and tedious.
///
/// For that reason, the parser tries to convert every value into a `String`. If an
/// attribute can contain unconstrained binary strings, the conversion may fail. In that case,
/// the attribute and all its values will be in the `bin_attrs` hashmap. Since it's
/// possible that a particular set of values for a binary attribute _could_ be
/// converted into UTF-8 `String`s, the presence of of such attribute in the result
/// entry should be checked for both in `attrs` and `bin_atrrs`.
///
/// In the future versions of the library, this parsing interface will be
/// de-emphasized in favor of custom Serde deserialization of search results directly
/// into a user-supplied struct, which is expected to be a better fit for the
/// majority of uses.
#[derive(Debug, Clone)]
pub struct SearchEntry {
    /// Entry DN.
    pub dn: String,
    /// Attributes.
    pub attrs: HashMap<String, Vec<String>>,
    /// Binary-valued attributes.
    pub bin_attrs: HashMap<String, Vec<Vec<u8>>>,
}

impl SearchEntry {
    /// Parse raw BER data and convert it into attribute map(s).
    ///
    /// __Note__: this function will panic on parsing error. Error handling will be
    /// improved in a future version of the library.
    pub fn construct(re: ResultEntry) -> SearchEntry {
        let mut tags =
            re.0.match_id(4)
                .and_then(|t| t.expect_constructed())
                .expect("entry")
                .into_iter();
        let dn = String::from_utf8(
            tags.next()
                .expect("element")
                .expect_primitive()
                .expect("octet string"),
        )
        .expect("dn");
        let mut attr_vals = HashMap::new();
        let mut bin_attr_vals = HashMap::new();
        let attrs = tags
            .next()
            .expect("element")
            .expect_constructed()
            .expect("attrs")
            .into_iter();
        for a_v in attrs {
            let mut part_attr = a_v
                .expect_constructed()
                .expect("partial attribute")
                .into_iter();
            let a_type = String::from_utf8(
                part_attr
                    .next()
                    .expect("element")
                    .expect_primitive()
                    .expect("octet string"),
            )
            .expect("attribute type");
            let mut any_binary = false;
            let values = part_attr
                .next()
                .expect("element")
                .expect_constructed()
                .expect("values")
                .into_iter()
                .map(|t| t.expect_primitive().expect("octet string"))
                .filter_map(|s| {
                    if let Ok(s) = std::str::from_utf8(s.as_ref()) {
                        return Some(s.to_owned());
                    }
                    bin_attr_vals
                        .entry(a_type.clone())
                        .or_insert_with(|| vec![])
                        .push(s);
                    any_binary = true;
                    None
                })
                .collect::<Vec<String>>();
            if any_binary {
                bin_attr_vals.get_mut(&a_type).expect("bin vector").extend(
                    values
                        .into_iter()
                        .map(String::into_bytes)
                        .collect::<Vec<Vec<u8>>>(),
                );
            } else {
                attr_vals.insert(a_type, values);
            }
        }
        SearchEntry {
            dn: dn,
            attrs: attr_vals,
            bin_attrs: bin_attr_vals,
        }
    }
}

#[derive(Debug)]
pub struct SearchStream {
    ldap: Ldap,
    rx: Option<mpsc::UnboundedReceiver<SearchItem>>,
    req: Option<Tag>,
    timeout: Option<Duration>,
    res: Option<LdapResult>,
}

impl SearchStream {
    pub(crate) fn new(ldap: Ldap) -> Self {
        SearchStream {
            ldap,
            rx: None,
            req: None,
            timeout: None,
            res: None,
        }
    }

    /// See also [`LdapConn::streaming_search()`](struct.LdapConn.html#method.streaming_search).
    ///
    /// The returned future resolves to a [`SearchStream`](struct.SearchStream.html),
    /// which should be iterated through to obtain results. Before starting the iteration,
    /// the receiver future, which will yield the overall result of the search after the stream
    /// is drained, should be retrieved from the stream instance with
    /// [`get_result_rx()`](struct.SearchStream.html#method.get_result_rx). The stream and
    /// the receiver should be polled concurrently with `Future::join()`.
    pub(crate) async fn start<S: AsRef<str>>(
        mut self,
        base: &str,
        scope: Scope,
        filter: &str,
        attrs: Vec<S>,
    ) -> Result<Self> {
        let opts = match self.ldap.search_opts.take() {
            Some(opts) => opts,
            None => SearchOptions::new(),
        };
        self.timeout = self.ldap.timeout.take();
        let req = Tag::Sequence(Sequence {
            id: 3,
            class: TagClass::Application,
            inner: vec![
                Tag::OctetString(OctetString {
                    inner: Vec::from(base.as_bytes()),
                    ..Default::default()
                }),
                Tag::Enumerated(Enumerated {
                    inner: scope as i64,
                    ..Default::default()
                }),
                Tag::Enumerated(Enumerated {
                    inner: opts.deref as i64,
                    ..Default::default()
                }),
                Tag::Integer(Integer {
                    inner: opts.sizelimit as i64,
                    ..Default::default()
                }),
                Tag::Integer(Integer {
                    inner: opts.timelimit as i64,
                    ..Default::default()
                }),
                Tag::Boolean(Boolean {
                    inner: opts.typesonly,
                    ..Default::default()
                }),
                match parse_filter(filter) {
                    Ok(filter) => filter,
                    _ => return Err(LdapError::FilterParsing),
                },
                Tag::Sequence(Sequence {
                    inner: attrs
                        .into_iter()
                        .map(|s| {
                            Tag::OctetString(OctetString {
                                inner: Vec::from(s.as_ref()),
                                ..Default::default()
                            })
                        })
                        .collect(),
                    ..Default::default()
                }),
            ],
        });
        let (tx, rx) = mpsc::unbounded_channel();
        self.rx = Some(rx);
        if let Some(ref timeout) = self.timeout {
            self.ldap.with_timeout(*timeout);
        }
        self.ldap.op_call(LdapOp::Search(tx), req).await?;
        Ok(self)
    }

    pub async fn next(&mut self) -> Result<Option<ResultEntry>> {
        if self.rx.is_none() {
            return Ok(None);
        }
        let item = if let Some(ref timeout) = self.timeout {
            time::timeout(*timeout, self.rx.as_mut().unwrap().recv()).await?
        } else {
            self.rx.as_mut().unwrap().recv().await
        };
        let item = match item {
            Some(item) => item,
            None => {
                self.rx = None;
                return Err(LdapError::EndOfStream);
            }
        };
        match item {
            SearchItem::Entry(tag) | SearchItem::Referral(tag) => {
                return Ok(Some(ResultEntry(tag)))
            }
            SearchItem::Done(mut res, controls) => {
                res.ctrls = controls;
                self.res = Some(res);
                self.rx = None;
            }
        }
        Ok(None)
    }

    pub fn finish(mut self) -> LdapResult {
        self.rx = None;
        self.res.unwrap_or_else(|| LdapResult {
            rc: 88,
            matched: String::from(""),
            text: String::from("user cancelled"),
            refs: vec![],
            ctrls: vec![],
        })
    }
}

#[derive(Clone, Debug)]
pub enum SyncInfo {
    NewCookie(Vec<u8>),
    RefreshDelete {
        cookie: Option<Vec<u8>>,
        refresh_done: bool,
    },
    RefreshPresent {
        cookie: Option<Vec<u8>>,
        refresh_done: bool,
    },
    SyncIdSet {
        cookie: Option<Vec<u8>>,
        refresh_deletes: bool,
        sync_uuids: HashSet<Vec<u8>>,
    },
}

pub fn parse_syncinfo<V: AsRef<[u8]>>(raw: V) -> SyncInfo {
    let syncinfo_val = match parse_tag(raw.as_ref()) {
        IResult::Done(_, tag) => tag,
        _ => panic!("syncinfo_val: not a tag"),
    };
    match syncinfo_val {
        StructureTag { id, class, payload } if class == TagClass::Context && id < 4 => match id {
            0 => {
                let cookie = match payload {
                    PL::P(payload) => payload,
                    PL::C(_) => panic!(),
                };
                SyncInfo::NewCookie(cookie)
            }
            1 | 2 | 3 => {
                let mut syncinfo_val = match payload {
                    PL::C(payload) => payload,
                    PL::P(_) => panic!(),
                }
                .into_iter();
                let mut sync_cookie = None;
                let mut flag = id != 3;
                let mut uuids = HashSet::new();
                let mut pass = 1;
                'it: loop {
                    match syncinfo_val.next() {
                        None => break 'it,
                        Some(comp) => match comp {
                            StructureTag {
                                id,
                                class,
                                payload: _,
                            } if class == TagClass::Universal
                                && id == Types::OctetString as u64
                                && pass <= 1 =>
                            {
                                sync_cookie = comp.expect_primitive();
                            }
                            StructureTag {
                                id,
                                class,
                                payload: _,
                            } if class == TagClass::Universal
                                && id == Types::Boolean as u64
                                && pass <= 2 =>
                            {
                                flag = !(comp.expect_primitive().expect("octet string")[0] == 0);
                            }
                            StructureTag {
                                id,
                                class,
                                payload: _,
                            } if class == TagClass::Universal
                                && id == Types::Set as u64
                                && pass <= 3 =>
                            {
                                uuids = comp
                                    .expect_constructed()
                                    .expect("uuid set")
                                    .into_iter()
                                    .map(|u| u.expect_primitive().expect("octet string"))
                                    .collect();
                            }
                            _ => panic!(),
                        },
                    }
                    pass += 1;
                }
                match id {
                    1 => SyncInfo::RefreshDelete {
                        cookie: sync_cookie,
                        refresh_done: flag,
                    },
                    2 => SyncInfo::RefreshPresent {
                        cookie: sync_cookie,
                        refresh_done: flag,
                    },
                    3 => SyncInfo::SyncIdSet {
                        cookie: sync_cookie,
                        refresh_deletes: flag,
                        sync_uuids: uuids,
                    },
                    _ => panic!(),
                }
            }
            _ => panic!(),
        },
        _ => panic!("syncinfo id not 0-3"),
    }
}

pub fn parse_refs(t: StructureTag) -> Vec<String> {
    t.expect_constructed()
        .expect("referrals")
        .into_iter()
        .map(|t| t.expect_primitive().expect("octet string"))
        .map(String::from_utf8)
        .map(|s| s.expect("uri"))
        .collect()
}
<|endoftext|>