<commit_before>//! FIXME: write short doc here

mod load_cargo;
mod analysis_stats;
mod analysis_bench;
mod progress_report;

use std::{error::Error, fmt::Write, io::Read, path::PathBuf};

use pico_args::Arguments;
use ra_ide::{file_structure, Analysis};
use ra_prof::profile;
use ra_syntax::{AstNode, SourceFile};

type Result<T, E = Box<dyn Error + Send + Sync>> = std::result::Result<T, E>;

fn main() -> Result<()> {
    env_logger::try_init()?;

    let command = match Command::from_args()? {
        Ok(it) => it,
        Err(HelpPrinted) => return Ok(()),
    };
    match command {
        Command::Parse { no_dump } => {
            let _p = profile("parsing");
            let file = file()?;
            if !no_dump {
                println!("{:#?}", file.syntax());
            }
            std::mem::forget(file);
        }
        Command::Symbols => {
            let file = file()?;
            for s in file_structure(&file) {
                println!("{:?}", s);
            }
        }
        Command::Highlight { rainbow } => {
            let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
            let html = analysis.highlight_as_html(file_id, rainbow).unwrap();
            println!("{}", html);
        }
        Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => {
            analysis_stats::run(
                verbosity,
                memory_usage,
                path.as_ref(),
                only.as_ref().map(String::as_ref),
                with_deps,
                randomize,
            )?;
        }
        Command::Bench { verbosity, path, op } => {
            analysis_bench::run(verbosity, path.as_ref(), op)?;
        }
    }

    Ok(())
}

#[derive(Clone, Copy)]
pub enum Verbosity {
    Spammy,
    Verbose,
    Normal,
    Quiet,
}

impl Verbosity {
    fn is_verbose(self) -> bool {
        match self {
            Verbosity::Verbose | Verbosity::Spammy => true,
            _ => false,
        }
    }
    fn is_spammy(self) -> bool {
        match self {
            Verbosity::Spammy => true,
            _ => false,
        }
    }
}

enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        verbosity: Verbosity,
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: PathBuf,
    },
    Bench {
        verbosity: Verbosity,
        path: PathBuf,
        op: analysis_bench::Op,
    },
}

struct HelpPrinted;

impl Command {
    fn from_args() -> Result<Result<Command, HelpPrinted>> {
        let mut matches = Arguments::from_env();
        let subcommand = matches.subcommand()?.unwrap_or_default();

        let verbosity = match (
            matches.contains(["-vv", "--spammy"]),
            matches.contains(["-v", "--verbose"]),
            matches.contains(["-q", "--quiet"]),
        ) {
            (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?,
            (true, _, false) => Verbosity::Spammy,
            (false, false, false) => Verbosity::Normal,
            (false, false, true) => Verbosity::Quiet,
            (false, true, false) => Verbosity::Verbose,
            (false, true, true) => Err("Invalid flags: -q conflicts with -v")?,
        };

        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    ra_cli parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Err(HelpPrinted));
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    ra_cli analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        Err("Invalid flags")?;
                    }
                    trailing.pop().unwrap().into()
                };

                Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra_cli-analysis-bench

USAGE:
    ra_cli analysis-bench [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<String> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?;
                let op = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() },
                    (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?),
                    (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { verbosity, path, op }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    ra_cli <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Err(HelpPrinted));
            }
        };
        Ok(Ok(command))
    }
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn file() -> Result<SourceFile> {
    let text = read_stdin()?;
    Ok(SourceFile::parse(&text).tree())
}

fn read_stdin() -> Result<String> {
    let mut buff = String::new();
    std::io::stdin().read_to_string(&mut buff)?;
    Ok(buff)
}
<commit_msg>Better name<commit_after>//! FIXME: write short doc here

mod load_cargo;
mod analysis_stats;
mod analysis_bench;
mod progress_report;

use std::{error::Error, fmt::Write, io::Read, path::PathBuf};

use pico_args::Arguments;
use ra_ide::{file_structure, Analysis};
use ra_prof::profile;
use ra_syntax::{AstNode, SourceFile};

type Result<T, E = Box<dyn Error + Send + Sync>> = std::result::Result<T, E>;

fn main() -> Result<()> {
    env_logger::try_init()?;

    let command = match Command::from_env_args()? {
        Ok(it) => it,
        Err(HelpPrinted) => return Ok(()),
    };
    match command {
        Command::Parse { no_dump } => {
            let _p = profile("parsing");
            let file = file()?;
            if !no_dump {
                println!("{:#?}", file.syntax());
            }
            std::mem::forget(file);
        }
        Command::Symbols => {
            let file = file()?;
            for s in file_structure(&file) {
                println!("{:?}", s);
            }
        }
        Command::Highlight { rainbow } => {
            let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
            let html = analysis.highlight_as_html(file_id, rainbow).unwrap();
            println!("{}", html);
        }
        Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => {
            analysis_stats::run(
                verbosity,
                memory_usage,
                path.as_ref(),
                only.as_ref().map(String::as_ref),
                with_deps,
                randomize,
            )?;
        }
        Command::Bench { verbosity, path, op } => {
            analysis_bench::run(verbosity, path.as_ref(), op)?;
        }
    }

    Ok(())
}

#[derive(Clone, Copy)]
pub enum Verbosity {
    Spammy,
    Verbose,
    Normal,
    Quiet,
}

impl Verbosity {
    fn is_verbose(self) -> bool {
        match self {
            Verbosity::Verbose | Verbosity::Spammy => true,
            _ => false,
        }
    }
    fn is_spammy(self) -> bool {
        match self {
            Verbosity::Spammy => true,
            _ => false,
        }
    }
}

enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        verbosity: Verbosity,
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: PathBuf,
    },
    Bench {
        verbosity: Verbosity,
        path: PathBuf,
        op: analysis_bench::Op,
    },
}

struct HelpPrinted;

impl Command {
    fn from_env_args() -> Result<Result<Command, HelpPrinted>> {
        let mut matches = Arguments::from_env();
        let subcommand = matches.subcommand()?.unwrap_or_default();

        let verbosity = match (
            matches.contains(["-vv", "--spammy"]),
            matches.contains(["-v", "--verbose"]),
            matches.contains(["-q", "--quiet"]),
        ) {
            (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?,
            (true, _, false) => Verbosity::Spammy,
            (false, false, false) => Verbosity::Normal,
            (false, false, true) => Verbosity::Quiet,
            (false, true, false) => Verbosity::Verbose,
            (false, true, true) => Err("Invalid flags: -q conflicts with -v")?,
        };

        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    ra_cli parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Err(HelpPrinted));
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    ra_cli analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        Err("Invalid flags")?;
                    }
                    trailing.pop().unwrap().into()
                };

                Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra_cli-analysis-bench

USAGE:
    ra_cli analysis-bench [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<String> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?;
                let op = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() },
                    (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?),
                    (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { verbosity, path, op }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    ra_cli <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Err(HelpPrinted));
            }
        };
        Ok(Ok(command))
    }
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn file() -> Result<SourceFile> {
    let text = read_stdin()?;
    Ok(SourceFile::parse(&text).tree())
}

fn read_stdin() -> Result<String> {
    let mut buff = String::new();
    std::io::stdin().read_to_string(&mut buff)?;
    Ok(buff)
}
<|endoftext|>


<commit_before>use cocoa::base::{class, id};
use cocoa::foundation::NSUInteger;
use core_foundation::base::CFRange;
use libc::c_void;
use objc::runtime::BOOL;
use {MTLPixelFormat, MTLRegion};

#[cfg(target_os = "macos")]
macro_rules! texture_platform_methods_impl {
    () => {
        unsafe fn ioSurface(self) -> id {
            msg_send![self, ioSurface]
        }
        unsafe fn ioSurfacePlane(self) -> NSUInteger {
            msg_send![self, ioSurfacePlane]
        }
    }
}

#[cfg(target_os = "ios")]
macro_rules! texture_platform_methods_impl {
    () => {
        unsafe fn buffer(self) -> id {
            msg_send![self, buffer]
        }

        unsafe fn bufferOffset(self) -> NSUInteger {
            msg_send![self, bufferOffset]
        }

        unsafe fn bufferBytesPerRow(self) -> id {
            msg_send![self, bufferBytesPerRow]
        }
    }
}

#[cfg(target_os = "macos")]
pub trait MTLTexture {
    unsafe fn replaceRegion_mipmapLevel_slice_withBytes_bytesPerRow_bytesPerImage(
        self, region: MTLRegion, level: NSUInteger, slice: NSUInteger,
        pixelBytes: *const c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger);

    unsafe fn replaceRegion_mipmapLevel_withBytes_bytesPerRow(self, region: MTLRegion,
                                                              level: NSUInteger,
                                                              pixelBytes: *mut c_void,
                                                              bytesPerRow: NSUInteger);

    unsafe fn getBytes_bytesPerRow_bytesPerImage_fromRegion_mipmapLevel_slice(
        self, pixel_bytes: *mut c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger, region: MTLRegion, mipmapLevel: NSUInteger,
        slice: NSUInteger);

    unsafe fn getBytes_bytesPerRow_fromRegion_mipmapLevel(self, pixel_bytes: *mut c_void,
                                                          bytesPerRow: NSUInteger,
                                                          region: MTLRegion,
                                                          mipmapLevel: NSUInteger);

    unsafe fn newTextureViewWithPixelFormat(format: MTLPixelFormat) -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format]
    }

    unsafe fn newTextureViewWithPixelFormat_textureType_levels_slices(format: MTLPixelFormat,
                                                                      textureType: MTLTextureType,
                                                                      levels: CFRange,
                                                                      slices: NSUInteger)
                                                                      -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format
                                                         textureType:textureType
                                                              levels:levels
                                                              slices:slices]
    }

    unsafe fn textureType(self) -> MTLTextureType;
    unsafe fn pixelFormat(self) -> MTLPixelFormat;
    unsafe fn width(self) -> NSUInteger;
    unsafe fn height(self) -> NSUInteger;
    unsafe fn depth(self) -> NSUInteger;
    unsafe fn mipmapLevelCount(self) -> NSUInteger;
    unsafe fn arrayLength(self) -> NSUInteger;
    unsafe fn sampleCount(self) -> NSUInteger;
    unsafe fn frameBufferOnly(self) -> BOOL;
    unsafe fn rootResource(self) -> id;
    unsafe fn usage(self) -> MTLTextureUsage;

    unsafe fn ioSurface(self) -> id;
    unsafe fn ioSurfacePlane(self) -> NSUInteger;

    unsafe fn parentTexture(self) -> id;
    unsafe fn parentRelativeLevel(self) -> NSUInteger;
    unsafe fn parentRelativeSlice(self) -> NSUInteger;
}

#[cfg(target_os = "ios")]
pub trait MTLTexture {
    unsafe fn replaceRegion_mipmapLevel_slice_withBytes_bytesPerRow_bytesPerImage(
        self, region: MTLRegion, level: NSUInteger, slice: NSUInteger,
        pixelBytes: *const c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger);

    unsafe fn replaceRegion_mipmapLevel_withBytes_bytesPerRow(self, region: MTLRegion,
                                                              level: NSUInteger,
                                                              pixelBytes: *mut c_void,
                                                              bytesPerRow: NSUInteger);

    unsafe fn getBytes_bytesPerRow_bytesPerImage_fromRegion_mipmapLevel_slice(
        self, pixel_bytes: *mut c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger, region: MTLRegion, mipmapLevel: NSUInteger,
        slice: NSUInteger);

    unsafe fn getBytes_bytesPerRow_fromRegion_mipmapLevel(self, pixel_bytes: *mut c_void,
                                                          bytesPerRow: NSUInteger,
                                                          region: MTLRegion,
                                                          mipmapLevel: NSUInteger);

    unsafe fn newTextureViewWithPixelFormat(format: MTLPixelFormat) -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format]
    }

    unsafe fn newTextureViewWithPixelFormat_textureType_levels_slices(format: MTLPixelFormat,
                                                                      textureType: MTLTextureType,
                                                                      levels: CFRange,
                                                                      slices: NSUInteger)
                                                                      -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format
                                                         textureType:textureType
                                                              levels:levels
                                                              slices:slices]
    }

    unsafe fn textureType(self) -> MTLTextureType;
    unsafe fn pixelFormat(self) -> MTLPixelFormat;
    unsafe fn width(self) -> NSUInteger;
    unsafe fn height(self) -> NSUInteger;
    unsafe fn depth(self) -> NSUInteger;
    unsafe fn mipmapLevelCount(self) -> NSUInteger;
    unsafe fn arrayLength(self) -> NSUInteger;
    unsafe fn sampleCount(self) -> NSUInteger;
    unsafe fn frameBufferOnly(self) -> BOOL;
    unsafe fn rootResource(self) -> id;
    unsafe fn usage(self) -> MTLTextureUsage;

    unsafe fn buffer(self) -> id;
    unsafe fn bufferOffset(self) -> NSUInteger;
    unsafe fn bufferBytesPerRow(self) -> NSUInteger;

    unsafe fn parentTexture(self) -> id;
    unsafe fn parentRelativeLevel(self) -> NSUInteger;
    unsafe fn parentRelativeSlice(self) -> NSUInteger;
}

impl MTLTexture for id {
    unsafe fn replaceRegion_mipmapLevel_slice_withBytes_bytesPerRow_bytesPerImage(
        self, region: MTLRegion, level: NSUInteger, slice: NSUInteger,
        pixelBytes: *const c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger) {
        msg_send![self, replaceRegion:region
                          mipmapLevel:level
                                slice:slice
                            withBytes:pixelBytes
                          bytesPerRow:bytesPerRow
                        bytesPerImage:bytesPerImage]
    }
    unsafe fn replaceRegion_mipmapLevel_withBytes_bytesPerRow(self, region: MTLRegion,
                                                              level: NSUInteger,
                                                              pixelBytes: *mut c_void,
                                                              bytesPerRow: NSUInteger) {
        msg_send![self, replaceRegion:region
                          mipmapLevel:level
                            withBytes:pixelBytes
                          bytesPerRow:bytesPerRow]
    }

    unsafe fn getBytes_bytesPerRow_bytesPerImage_fromRegion_mipmapLevel_slice(
        self, pixelBytes: *mut c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger, region: MTLRegion, level: NSUInteger,
        slice: NSUInteger) {
        msg_send![self, getBytes:pixelBytes
                     bytesPerRow:bytesPerRow
                   bytesPerImage:bytesPerImage
                          region:region
                     mipmapLevel:level
                           slice:slice]
    }

    unsafe fn getBytes_bytesPerRow_fromRegion_mipmapLevel(self, pixelBytes: *mut c_void,
                                                          bytesPerRow: NSUInteger,
                                                          region: MTLRegion, level: NSUInteger) {
        msg_send![self, getBytes:pixelBytes
                     bytesPerRow:bytesPerRow
                          region:region
                     mipmapLevel:level]
    }

    unsafe fn textureType(self) -> MTLTextureType {
        msg_send![self, textureType]
    }

    unsafe fn pixelFormat(self) -> MTLPixelFormat {
        msg_send![self, pixelFormat]
    }

    unsafe fn width(self) -> NSUInteger {
        msg_send![self, width]
    }

    unsafe fn height(self) -> NSUInteger {
        msg_send![self, height]
    }

    unsafe fn depth(self) -> NSUInteger {
        msg_send![self, depth]
    }

    unsafe fn mipmapLevelCount(self) -> NSUInteger {
        msg_send![self, mipmapLevelCount]
    }

    unsafe fn arrayLength(self) -> NSUInteger {
        msg_send![self, arrayLength]
    }

    unsafe fn sampleCount(self) -> NSUInteger {
        msg_send![self, sampleCount]
    }

    unsafe fn frameBufferOnly(self) -> BOOL {
        msg_send![self, frameBufferOnly]
    }

    unsafe fn rootResource(self) -> id {
        msg_send![self, rootResource]
    }

    unsafe fn usage(self) -> MTLTextureUsage {
        msg_send![self, usage]
    }

    unsafe fn parentTexture(self) -> id {
        msg_send![self, parentTexture]
    }

    unsafe fn parentRelativeLevel(self) -> NSUInteger {
        msg_send![self, parentRelativeLevel]
    }

    unsafe fn parentRelativeSlice(self) -> NSUInteger {
        msg_send![self, parentRelativeSlice]
    }

    texture_platform_methods_impl!{}
}

#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MTLTextureType {
    MTLTextureType1D = 0,
    MTLTextureType1DArray = 1,
    MTLTextureType2D = 2,
    MTLTextureType2DArray = 3,
    MTLTextureType2DMultisample = 4,
    MTLTextureTypeCube = 5,
    MTLTextureType3D = 7
}

#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MTLTextureUsage {
    MTLTextureUsageUnknown = 0x0000,
    MTLTextureUsageShaderRead = 0x0001,
    MTLTextureUsageShaderWrite = 0x0002,
    MTLTextureUsageRenderTarget = 0x0004,
    MTLTextureUsagePixelFormatView = 0x0010
}
<commit_msg>Fix some signatures<commit_after>use cocoa::base::{class, id};
use cocoa::foundation::NSUInteger;
use core_foundation::base::CFRange;
use libc::c_void;
use objc::runtime::BOOL;
use {MTLPixelFormat, MTLRegion};

#[cfg(target_os = "macos")]
macro_rules! texture_platform_methods_impl {
    () => {
        unsafe fn ioSurface(self) -> id {
            msg_send![self, ioSurface]
        }
        unsafe fn ioSurfacePlane(self) -> NSUInteger {
            msg_send![self, ioSurfacePlane]
        }
    }
}

#[cfg(target_os = "ios")]
macro_rules! texture_platform_methods_impl {
    () => {
        unsafe fn buffer(self) -> id {
            msg_send![self, buffer]
        }

        unsafe fn bufferOffset(self) -> NSUInteger {
            msg_send![self, bufferOffset]
        }

        unsafe fn bufferBytesPerRow(self) -> id {
            msg_send![self, bufferBytesPerRow]
        }
    }
}

#[cfg(target_os = "macos")]
pub trait MTLTexture {
    unsafe fn replaceRegion_mipmapLevel_slice_withBytes_bytesPerRow_bytesPerImage(
        self, region: MTLRegion, level: NSUInteger, slice: NSUInteger,
        pixelBytes: *const c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger);

    unsafe fn replaceRegion_mipmapLevel_withBytes_bytesPerRow(self, region: MTLRegion,
                                                              level: NSUInteger,
                                                              pixelBytes: *const c_void,
                                                              bytesPerRow: NSUInteger);

    unsafe fn getBytes_bytesPerRow_bytesPerImage_fromRegion_mipmapLevel_slice(
        self, pixel_bytes: *mut c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger, region: MTLRegion, mipmapLevel: NSUInteger,
        slice: NSUInteger);

    unsafe fn getBytes_bytesPerRow_fromRegion_mipmapLevel(self, pixel_bytes: *mut c_void,
                                                          bytesPerRow: NSUInteger,
                                                          region: MTLRegion,
                                                          mipmapLevel: NSUInteger);

    unsafe fn newTextureViewWithPixelFormat(_: Self, format: MTLPixelFormat) -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format]
    }

    unsafe fn newTextureViewWithPixelFormat_textureType_levels_slices(
        _: Self, format: MTLPixelFormat, textureType: MTLTextureType,
        levels: CFRange, slices: NSUInteger) -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format
                                                         textureType:textureType
                                                              levels:levels
                                                              slices:slices]
    }

    unsafe fn textureType(self) -> MTLTextureType;
    unsafe fn pixelFormat(self) -> MTLPixelFormat;
    unsafe fn width(self) -> NSUInteger;
    unsafe fn height(self) -> NSUInteger;
    unsafe fn depth(self) -> NSUInteger;
    unsafe fn mipmapLevelCount(self) -> NSUInteger;
    unsafe fn arrayLength(self) -> NSUInteger;
    unsafe fn sampleCount(self) -> NSUInteger;
    unsafe fn frameBufferOnly(self) -> BOOL;
    unsafe fn rootResource(self) -> id;
    unsafe fn usage(self) -> MTLTextureUsage;

    unsafe fn ioSurface(self) -> id;
    unsafe fn ioSurfacePlane(self) -> NSUInteger;

    unsafe fn parentTexture(self) -> id;
    unsafe fn parentRelativeLevel(self) -> NSUInteger;
    unsafe fn parentRelativeSlice(self) -> NSUInteger;
}

#[cfg(target_os = "ios")]
pub trait MTLTexture {
    unsafe fn replaceRegion_mipmapLevel_slice_withBytes_bytesPerRow_bytesPerImage(
        self, region: MTLRegion, level: NSUInteger, slice: NSUInteger,
        pixelBytes: *const c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger);

    unsafe fn replaceRegion_mipmapLevel_withBytes_bytesPerRow(self, region: MTLRegion,
                                                              level: NSUInteger,
                                                              pixelBytes: *mut c_void,
                                                              bytesPerRow: NSUInteger);

    unsafe fn getBytes_bytesPerRow_bytesPerImage_fromRegion_mipmapLevel_slice(
        self, pixel_bytes: *mut c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger, region: MTLRegion, mipmapLevel: NSUInteger,
        slice: NSUInteger);

    unsafe fn getBytes_bytesPerRow_fromRegion_mipmapLevel(self, pixel_bytes: *mut c_void,
                                                          bytesPerRow: NSUInteger,
                                                          region: MTLRegion,
                                                          mipmapLevel: NSUInteger);

    unsafe fn newTextureViewWithPixelFormat(format: MTLPixelFormat) -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format]
    }

    unsafe fn newTextureViewWithPixelFormat_textureType_levels_slices(format: MTLPixelFormat,
                                                                      textureType: MTLTextureType,
                                                                      levels: CFRange,
                                                                      slices: NSUInteger)
                                                                      -> id {
        msg_send![class("MTLTexture"), newTextureViewWithPixelFormat:format
                                                         textureType:textureType
                                                              levels:levels
                                                              slices:slices]
    }

    unsafe fn textureType(self) -> MTLTextureType;
    unsafe fn pixelFormat(self) -> MTLPixelFormat;
    unsafe fn width(self) -> NSUInteger;
    unsafe fn height(self) -> NSUInteger;
    unsafe fn depth(self) -> NSUInteger;
    unsafe fn mipmapLevelCount(self) -> NSUInteger;
    unsafe fn arrayLength(self) -> NSUInteger;
    unsafe fn sampleCount(self) -> NSUInteger;
    unsafe fn frameBufferOnly(self) -> BOOL;
    unsafe fn rootResource(self) -> id;
    unsafe fn usage(self) -> MTLTextureUsage;

    unsafe fn buffer(self) -> id;
    unsafe fn bufferOffset(self) -> NSUInteger;
    unsafe fn bufferBytesPerRow(self) -> NSUInteger;

    unsafe fn parentTexture(self) -> id;
    unsafe fn parentRelativeLevel(self) -> NSUInteger;
    unsafe fn parentRelativeSlice(self) -> NSUInteger;
}

impl MTLTexture for id {
    unsafe fn replaceRegion_mipmapLevel_slice_withBytes_bytesPerRow_bytesPerImage(
        self, region: MTLRegion, level: NSUInteger, slice: NSUInteger,
        pixelBytes: *const c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger) {
        msg_send![self, replaceRegion:region
                          mipmapLevel:level
                                slice:slice
                            withBytes:pixelBytes
                          bytesPerRow:bytesPerRow
                        bytesPerImage:bytesPerImage]
    }
    unsafe fn replaceRegion_mipmapLevel_withBytes_bytesPerRow(self, region: MTLRegion,
                                                              level: NSUInteger,
                                                              pixelBytes: *const c_void,
                                                              bytesPerRow: NSUInteger) {
        msg_send![self, replaceRegion:region
                          mipmapLevel:level
                            withBytes:pixelBytes
                          bytesPerRow:bytesPerRow]
    }

    unsafe fn getBytes_bytesPerRow_bytesPerImage_fromRegion_mipmapLevel_slice(
        self, pixelBytes: *mut c_void, bytesPerRow: NSUInteger,
        bytesPerImage: NSUInteger, region: MTLRegion, level: NSUInteger,
        slice: NSUInteger) {
        msg_send![self, getBytes:pixelBytes
                     bytesPerRow:bytesPerRow
                   bytesPerImage:bytesPerImage
                          region:region
                     mipmapLevel:level
                           slice:slice]
    }

    unsafe fn getBytes_bytesPerRow_fromRegion_mipmapLevel(self, pixelBytes: *mut c_void,
                                                          bytesPerRow: NSUInteger,
                                                          region: MTLRegion, level: NSUInteger) {
        msg_send![self, getBytes:pixelBytes
                     bytesPerRow:bytesPerRow
                          region:region
                     mipmapLevel:level]
    }

    unsafe fn textureType(self) -> MTLTextureType {
        msg_send![self, textureType]
    }

    unsafe fn pixelFormat(self) -> MTLPixelFormat {
        msg_send![self, pixelFormat]
    }

    unsafe fn width(self) -> NSUInteger {
        msg_send![self, width]
    }

    unsafe fn height(self) -> NSUInteger {
        msg_send![self, height]
    }

    unsafe fn depth(self) -> NSUInteger {
        msg_send![self, depth]
    }

    unsafe fn mipmapLevelCount(self) -> NSUInteger {
        msg_send![self, mipmapLevelCount]
    }

    unsafe fn arrayLength(self) -> NSUInteger {
        msg_send![self, arrayLength]
    }

    unsafe fn sampleCount(self) -> NSUInteger {
        msg_send![self, sampleCount]
    }

    unsafe fn frameBufferOnly(self) -> BOOL {
        msg_send![self, frameBufferOnly]
    }

    unsafe fn rootResource(self) -> id {
        msg_send![self, rootResource]
    }

    unsafe fn usage(self) -> MTLTextureUsage {
        msg_send![self, usage]
    }

    unsafe fn parentTexture(self) -> id {
        msg_send![self, parentTexture]
    }

    unsafe fn parentRelativeLevel(self) -> NSUInteger {
        msg_send![self, parentRelativeLevel]
    }

    unsafe fn parentRelativeSlice(self) -> NSUInteger {
        msg_send![self, parentRelativeSlice]
    }

    texture_platform_methods_impl!{}
}

#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MTLTextureType {
    MTLTextureType1D = 0,
    MTLTextureType1DArray = 1,
    MTLTextureType2D = 2,
    MTLTextureType2DArray = 3,
    MTLTextureType2DMultisample = 4,
    MTLTextureTypeCube = 5,
    MTLTextureType3D = 7
}

#[repr(usize)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum MTLTextureUsage {
    MTLTextureUsageUnknown = 0x0000,
    MTLTextureUsageShaderRead = 0x0001,
    MTLTextureUsageShaderWrite = 0x0002,
    MTLTextureUsageRenderTarget = 0x0004,
    MTLTextureUsagePixelFormatView = 0x0010
}
<|endoftext|>


<commit_before>
extern crate clap;
extern crate intcode;

// Time Start: Thu, 26 Dec 2019 15:07:33 -0500
// Time Finish 1: Thu, 26 Dec 2019 15:47:02 -0500 (39 minutes, 29 seconds)
// Time Finish 2:
// Time Total:

use std::collections::HashSet;

use clap::{Arg, App};

use intcode::Intcode;

struct Network {
    computers: Vec<Intcode>,
    nat: (i64, i64),
}
impl Network {
    pub fn new(fname: &String, num: i64) -> Network {
        let mut comps = Vec::new();
        for i in 0..num {
            let mut ic = Intcode::load(&fname);
            ic.pipe(i);
            ic.nbinput(Some(-1));
            comps.push(ic);
        }
        return Network { computers: comps, nat: (0, 0) };
    }

    pub fn step(&mut self) -> Option<i64> {
        let num_comp = self.computers.len();
        let mut blocked = 0;
        for i in 0..self.computers.len() {
            let comp = &mut self.computers[i];
            comp.step();
            if comp.nbread_count() > 1 && comp.input_len() == 0 { blocked += 1; }
            if comp.output_len() >= 3 {
                let n = comp.shift_output().unwrap_or_else(|| panic!("expected 1 output!")) as usize;
                let x = comp.shift_output().unwrap_or_else(|| panic!("expected 1 output!"));
                let y = comp.shift_output().unwrap_or_else(|| panic!("expected 1 output!"));
                if n == 255 {
                    self.nat = (x, y);
                } else if n >= num_comp {
                    panic!("Bad address, sent {}, {} to address {}", x, y, n);
                } else {
                    self.computers[n].pipe(x);
                    self.computers[n].pipe(y);
                }
            }
        }

        if num_comp == blocked {
            self.computers[0].pipe(self.nat.0);
            self.computers[0].pipe(self.nat.1);
            return Some(self.nat.1)
        }
        return None;
    }
}

fn main() {
    let matches = App::new("Advent of Code 2019, Day 23")
        .arg(Arg::with_name("FILE")
             .help("Input file to process")
             .index(1))
        .get_matches();
    let fname = String::from(matches.value_of("FILE").unwrap_or("23.in"));

    let mut net = Network::new(&fname, 50);
    let mut seen = HashSet::new();
    loop {
        if let Some(y) = net.step() {
            if seen.len() == 0 {
                println!("First Y-value sent to NAT: {}", y);
            }
            if !seen.insert(y) {
                println!("First duplicated Y-value sent to NAT: {}", y);
                break;
            }
        }
    };
}
<commit_msg>Day 23<commit_after>
extern crate clap;
extern crate intcode;

// Time Start: Thu, 26 Dec 2019 15:07:33 -0500
// Time Finish 1: Thu, 26 Dec 2019 15:47:02 -0500 (39 minutes, 29 seconds)
// Time Finish 2: Thu, 26 Dec 2019 16:03:47 -0500 (16 minutes, 45 seconds)
// Time Total: 56 minutes, 14 seconds

use std::collections::HashSet;

use clap::{Arg, App};

use intcode::Intcode;

struct Network {
    computers: Vec<Intcode>,
    nat: (i64, i64),
}
impl Network {
    pub fn new(fname: &String, num: i64) -> Network {
        let mut comps = Vec::new();
        for i in 0..num {
            let mut ic = Intcode::load(&fname);
            ic.pipe(i);
            ic.nbinput(Some(-1));
            comps.push(ic);
        }
        return Network { computers: comps, nat: (0, 0) };
    }

    pub fn step(&mut self) -> Option<i64> {
        let num_comp = self.computers.len();
        let mut blocked = 0;
        for i in 0..self.computers.len() {
            let comp = &mut self.computers[i];
            comp.step();
            if comp.nbread_count() > 1 && comp.input_len() == 0 { blocked += 1; }
            if comp.output_len() >= 3 {
                let n = comp.shift_output().unwrap_or_else(|| panic!("expected 1 output!")) as usize;
                let x = comp.shift_output().unwrap_or_else(|| panic!("expected 1 output!"));
                let y = comp.shift_output().unwrap_or_else(|| panic!("expected 1 output!"));
                if n == 255 {
                    self.nat = (x, y);
                } else if n >= num_comp {
                    panic!("Bad address, sent {}, {} to address {}", x, y, n);
                } else {
                    self.computers[n].pipe(x);
                    self.computers[n].pipe(y);
                }
            }
        }

        if num_comp == blocked {
            self.computers[0].pipe(self.nat.0);
            self.computers[0].pipe(self.nat.1);
            return Some(self.nat.1)
        }
        return None;
    }
}

fn main() {
    let matches = App::new("Advent of Code 2019, Day 23")
        .arg(Arg::with_name("FILE")
             .help("Input file to process")
             .index(1))
        .get_matches();
    let fname = String::from(matches.value_of("FILE").unwrap_or("23.in"));

    let mut net = Network::new(&fname, 50);
    let mut seen = HashSet::new();
    loop {
        if let Some(y) = net.step() {
            if seen.len() == 0 {
                println!("First Y-value sent to NAT: {}", y);
            }
            if !seen.insert(y) {
                println!("First duplicated Y-value sent to NAT: {}", y);
                break;
            }
        }
    };
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::str::FromStr;

use hyper::Uri;

use client::{self, ApiClient};

static DATASTORE_ROOT: &str = "https://datastore.googleapis.com/v1";

pub struct DatastoreService {}
pub type Hub<'a> = client::Hub<'a, DatastoreService>;

pub type ValueMap = HashMap<String, Value>;

#[derive(Serialize, Default, Debug)]
pub struct BeginTransactionRequest {}

#[derive(Deserialize, Default, Debug)]
pub struct BeginTransactionResponse {
    pub transaction: String,
}

#[derive(Serialize, Default, Debug)]
pub struct CommitRequest {
    pub transaction: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub mode: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub mutations: Option<Vec<Mutation>>,
}

#[derive(Deserialize, Default, Debug)]
pub struct CommitResponse {
    #[serde(rename="mutationResults")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mutation_results: Option<Vec<MutationResult>>,

    #[serde(rename="indexUpdates")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub index_updates: Option<i32>,
}

#[derive(Serialize, Default, Debug)]
pub struct Mutation {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub insert: Option<Entity>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub upsert: Option<Entity>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub update: Option<Entity>,

    #[serde(rename="baseVersion")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_version: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub delete: Option<Key>,
}

#[derive(Deserialize, Default, Debug)]
pub struct MutationResult {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,

    #[serde(rename="conflictDetected")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub conflict_detected: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub key: Option<Key>,
}

#[derive(Serialize, Default, Debug)]
pub struct LookupRequest {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub keys: Option<Vec<Key>>,

    #[serde(rename="readOptions")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub read_options: Option<ReadOptions>,
}

#[derive(Deserialize, Default, Debug)]
pub struct LookupResponse {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub found: Option<Vec<EntityResult>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub missing: Option<Vec<EntityResult>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub deferred: Option<Vec<Key>>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct ReadOptions {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transaction: Option<String>,

    #[serde(rename="readConsistency")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub read_consistency: Option<String>,
}

#[derive(Deserialize, Default, Debug)]
pub struct EntityResult {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cursor: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity: Option<Entity>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Entity {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<HashMap<String, Value>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub key: Option<Key>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Key {
    pub path: Vec<PathElement>,

    #[serde(rename="partitionId")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub partition_id: Option<PartitionId>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct PathElement {
    pub kind: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct PartitionId {
    #[serde(rename="projectId")]
    pub project_id: String,

    #[serde(rename="namespaceId")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub namespace_id: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Value {
    #[serde(rename="entityValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_value: Option<Entity>,

    #[serde(rename="timestampValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp_value: Option<String>,

    #[serde(rename="stringValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub string_value: Option<String>,

    #[serde(rename="doubleValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub double_value: Option<f64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub meaning: Option<i32>,

    #[serde(rename="excludeFromIndexes")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub exclude_from_indexes: Option<bool>,

    #[serde(rename="blobValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub blob_value: Option<String>,

    #[serde(rename="keyValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_value: Option<Key>,

    #[serde(rename="booleanValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub boolean_value: Option<bool>,

    #[serde(rename="integerValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub integer_value: Option<String>,

    #[serde(rename="nullValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub null_value: Option<String>,

    //#[serde(rename="geoPointValue")]
    //#[serde(skip_serializing_if = "Option::is_none")]
    //pub geo_point_value: Option<LatLng>,

    //#[serde(rename="arrayValue")]
    //#[serde(skip_serializing_if = "Option::is_none")]
    //pub array_value: Option<ArrayValue>,
}

impl<'a> Hub<'a> {
    pub fn insert_entity_auto_id(&self,
                                 kind: &str,
                                 ns: &str,
                                 props: ValueMap)
                                 -> client::Result<String> {
        let key = self.mk_key(kind, Some(ns), None, None);
        let res = self.insert(key, props)?;
        let mut results = res.mutation_results.expect("mutations to be valid");

        let mut key = results.remove(0).key.expect("key to be valid");
        Ok(key.path.remove(0).id.expect("id to be valid"))
    }

    pub fn insert_entity_by_name(&self,
                                 kind: &str,
                                 ns: &str,
                                 name: &str,
                                 props: ValueMap)
                                 -> client::Result<()> {
        let key = self.mk_key(kind, Some(ns), Some(name), None);
        self.insert(key, props)?;
        Ok(())
    }

    fn insert(&self, key: Key, props: ValueMap) -> client::Result<CommitResponse> {
        let entity = Entity {
            key: Some(key),
            properties: Some(props),
        };

        let insert = Mutation {
            insert: Some(entity),
            ..Default::default()
        };

        let req = CommitRequest {
            mutations: Some(vec![insert]),
            ..Default::default()
        };

        let txn_id = self.begin_transaction()?;
        self.commit(&txn_id, req)
    }

    pub fn lookup_by_id(&self, kind: &str, ns: &str, id: &str) -> client::Result<Option<ValueMap>> {
        let key = self.mk_key(kind, Some(ns), None, Some(id));
        self.lookup_one(key)
    }

    pub fn lookup_by_name(&self,
                          kind: &str,
                          ns: &str,
                          name: &str)
                          -> client::Result<Option<ValueMap>> {
        let key = self.mk_key(kind, Some(ns), Some(name), None);
        self.lookup_one(key)
    }

    fn lookup_one(&self, key: Key) -> client::Result<Option<ValueMap>> {
        let req = LookupRequest {
            keys: Some(vec![key]),
            read_options: None,
        };

        let uri = self.mk_uri("lookup");
        let res = self.post::<_, LookupResponse>(&uri, req, &[])?;

        Ok(res.found
               .and_then(|mut f| if f.len() != 1 {
                             None
                         } else {
                             f.remove(0).entity
                         })
               .and_then(|e| e.properties))
    }

    pub fn update_by_id(&self,
                        kind: &str,
                        ns: &str,
                        id: &str,
                        props: ValueMap)
                        -> client::Result<()> {
        let key = self.mk_key(kind, Some(ns), None, Some(id));

        let entity = Entity {
            key: Some(key),
            properties: Some(props),
        };

        let update = Mutation {
            update: Some(entity),
            ..Default::default()
        };

        let req = CommitRequest {
            mutations: Some(vec![update]),
            ..Default::default()
        };

        let txn_id = self.begin_transaction()?;
        self.commit(&txn_id, req)?;

        Ok(())
    }

    pub fn delete_by_id(&self, kind: &str, ns: &str, id: &str) -> client::Result<()> {
        let key = self.mk_key(kind, Some(ns), None, Some(id));

        let delete = Mutation {
            delete: Some(key),
            ..Default::default()
        };

        let req = CommitRequest {
            mutations: Some(vec![delete]),
            ..Default::default()
        };

        let txn_id = self.begin_transaction()?;
        self.commit(&txn_id, req)?;

        Ok(())
    }

    fn begin_transaction(&self) -> client::Result<String> {
        let uri = self.mk_uri("beginTransaction");
        let req = BeginTransactionRequest::default();
        self.post::<_, BeginTransactionResponse>(&uri, req, &[])
            .map(|r| r.transaction)
    }

    fn commit(&self, txn_id: &str, mut req: CommitRequest) -> client::Result<CommitResponse> {
        req.transaction = txn_id.to_string();
        let uri = self.mk_uri("commit");
        self.post(&uri, req, &[])
    }

    fn mk_uri(&self, action: &str) -> Uri {
        let path = format!("{}/projects/{}:{}",
                           DATASTORE_ROOT,
                           self.project_id(),
                           action);
        Uri::from_str(&path).expect("uri to be valid")
    }

    fn mk_key(&self, kind: &str, ns: Option<&str>, name: Option<&str>, id: Option<&str>) -> Key {
        let partition_id = PartitionId {
            project_id: self.project_id().to_string(),
            namespace_id: ns.map(|ns| ns.to_string()),
        };

        let path = PathElement {
            kind: kind.to_string(),
            name: name.map(|name| name.to_string()),
            id: id.map(|id| id.to_string()),
        };

        Key {
            path: vec![path],
            partition_id: Some(partition_id),
        }
    }
}
<commit_msg>Review cleanup<commit_after>use std::collections::HashMap;
use std::str::FromStr;

use hyper::Uri;

use client::{self, ApiClient};

static DATASTORE_ROOT: &str = "https://datastore.googleapis.com/v1";

pub struct DatastoreService {}
pub type Hub<'a> = client::Hub<'a, DatastoreService>;

pub type ValueMap = HashMap<String, Value>;

#[derive(Serialize, Default, Debug)]
pub struct BeginTransactionRequest {}

#[derive(Deserialize, Default, Debug)]
pub struct BeginTransactionResponse {
    pub transaction: String,
}

#[derive(Serialize, Default, Debug)]
pub struct CommitRequest {
    pub transaction: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub mode: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub mutations: Option<Vec<Mutation>>,
}

#[derive(Deserialize, Default, Debug)]
pub struct CommitResponse {
    #[serde(rename="mutationResults")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mutation_results: Option<Vec<MutationResult>>,

    #[serde(rename="indexUpdates")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub index_updates: Option<i32>,
}

#[derive(Serialize, Default, Debug)]
pub struct Mutation {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub insert: Option<Entity>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub upsert: Option<Entity>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub update: Option<Entity>,

    #[serde(rename="baseVersion")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub base_version: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub delete: Option<Key>,
}

#[derive(Deserialize, Default, Debug)]
pub struct MutationResult {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,

    #[serde(rename="conflictDetected")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub conflict_detected: Option<bool>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub key: Option<Key>,
}

#[derive(Serialize, Default, Debug)]
pub struct LookupRequest {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub keys: Option<Vec<Key>>,

    #[serde(rename="readOptions")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub read_options: Option<ReadOptions>,
}

#[derive(Deserialize, Default, Debug)]
pub struct LookupResponse {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub found: Option<Vec<EntityResult>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub missing: Option<Vec<EntityResult>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub deferred: Option<Vec<Key>>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct ReadOptions {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transaction: Option<String>,

    #[serde(rename="readConsistency")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub read_consistency: Option<String>,
}

#[derive(Deserialize, Default, Debug)]
pub struct EntityResult {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cursor: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub version: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity: Option<Entity>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Entity {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub properties: Option<HashMap<String, Value>>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub key: Option<Key>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Key {
    pub path: Vec<PathElement>,

    #[serde(rename="partitionId")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub partition_id: Option<PartitionId>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct PathElement {
    pub kind: String,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<String>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct PartitionId {
    #[serde(rename="projectId")]
    pub project_id: String,

    #[serde(rename="namespaceId")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub namespace_id: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Value {
    #[serde(rename="entityValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub entity_value: Option<Entity>,

    #[serde(rename="timestampValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp_value: Option<String>,

    #[serde(rename="stringValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub string_value: Option<String>,

    #[serde(rename="doubleValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub double_value: Option<f64>,

    #[serde(skip_serializing_if = "Option::is_none")]
    pub meaning: Option<i32>,

    #[serde(rename="excludeFromIndexes")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub exclude_from_indexes: Option<bool>,

    #[serde(rename="blobValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub blob_value: Option<String>,

    #[serde(rename="keyValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub key_value: Option<Key>,

    #[serde(rename="booleanValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub boolean_value: Option<bool>,

    #[serde(rename="integerValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub integer_value: Option<String>,

    #[serde(rename="nullValue")]
    #[serde(skip_serializing_if = "Option::is_none")]
    pub null_value: Option<String>,

    //#[serde(rename="geoPointValue")]
    //#[serde(skip_serializing_if = "Option::is_none")]
    //pub geo_point_value: Option<LatLng>,

    //#[serde(rename="arrayValue")]
    //#[serde(skip_serializing_if = "Option::is_none")]
    //pub array_value: Option<ArrayValue>,
}

impl<'a> Hub<'a> {
    pub fn insert_entity_auto_id(&self,
                                 kind: &str,
                                 ns: &str,
                                 props: ValueMap)
                                 -> client::Result<String> {
        let key = self.mk_key(kind, Some(ns), None, None);
        let res = self.insert(key, props)?;
        let mut results = res.mutation_results.expect("mutations to be valid");

        let mut key = results.remove(0).key.expect("key to be valid");
        Ok(key.path.remove(0).id.expect("id to be valid"))
    }

    pub fn insert_entity_by_name(&self,
                                 kind: &str,
                                 ns: &str,
                                 name: &str,
                                 props: ValueMap)
                                 -> client::Result<()> {
        let key = self.mk_key(kind, Some(ns), Some(name), None);
        self.insert(key, props)?;
        Ok(())
    }

    fn insert(&self, key: Key, props: ValueMap) -> client::Result<CommitResponse> {
        let entity = Entity {
            key: Some(key),
            properties: Some(props),
        };

        let insert = Mutation {
            insert: Some(entity),
            ..Default::default()
        };

        let req = CommitRequest {
            mutations: Some(vec![insert]),
            ..Default::default()
        };

        let txn_id = self.begin_transaction()?;
        self.commit(&txn_id, req)
    }

    pub fn lookup_by_id(&self, kind: &str, ns: &str, id: &str) -> client::Result<Option<ValueMap>> {
        let key = self.mk_key(kind, Some(ns), None, Some(id));
        self.lookup_one(key)
    }

    pub fn lookup_by_name(&self,
                          kind: &str,
                          ns: &str,
                          name: &str)
                          -> client::Result<Option<ValueMap>> {
        let key = self.mk_key(kind, Some(ns), Some(name), None);
        self.lookup_one(key)
    }

    // Lookup a key using default read options:
    // https://cloud.google.com/datastore/docs/reference/rest/v1/ReadOptions
    fn lookup_one(&self, key: Key) -> client::Result<Option<ValueMap>> {
        let req = LookupRequest {
            keys: Some(vec![key]),
            read_options: None,
        };

        let uri = self.mk_uri("lookup");
        let res = self.post::<_, LookupResponse>(&uri, req, &[])?;

        Ok(res.found
               .and_then(|mut f| if f.len() != 1 {
                             None
                         } else {
                             f.remove(0).entity
                         })
               .and_then(|e| e.properties))
    }

    pub fn update_by_id(&self,
                        kind: &str,
                        ns: &str,
                        id: &str,
                        props: ValueMap)
                        -> client::Result<()> {
        let key = self.mk_key(kind, Some(ns), None, Some(id));

        let entity = Entity {
            key: Some(key),
            properties: Some(props),
        };

        let update = Mutation {
            update: Some(entity),
            ..Default::default()
        };

        let req = CommitRequest {
            mutations: Some(vec![update]),
            ..Default::default()
        };

        let txn_id = self.begin_transaction()?;
        self.commit(&txn_id, req)?;

        Ok(())
    }

    pub fn delete_by_id(&self, kind: &str, ns: &str, id: &str) -> client::Result<()> {
        let key = self.mk_key(kind, Some(ns), None, Some(id));

        let delete = Mutation {
            delete: Some(key),
            ..Default::default()
        };

        let req = CommitRequest {
            mutations: Some(vec![delete]),
            ..Default::default()
        };

        let txn_id = self.begin_transaction()?;
        self.commit(&txn_id, req)?;

        Ok(())
    }

    fn begin_transaction(&self) -> client::Result<String> {
        let uri = self.mk_uri("beginTransaction");
        let req = BeginTransactionRequest::default();
        self.post::<_, BeginTransactionResponse>(&uri, req, &[])
            .map(|r| r.transaction)
    }

    fn commit(&self, txn_id: &str, mut req: CommitRequest) -> client::Result<CommitResponse> {
        req.transaction = txn_id.to_string();
        let uri = self.mk_uri("commit");
        self.post(&uri, req, &[])
    }

    fn mk_uri(&self, action: &str) -> Uri {
        let path = format!("{}/projects/{}:{}",
                           DATASTORE_ROOT,
                           self.project_id(),
                           action);
        Uri::from_str(&path).expect("uri to be valid")
    }

    fn mk_key(&self, kind: &str, ns: Option<&str>, name: Option<&str>, id: Option<&str>) -> Key {
        let partition_id = PartitionId {
            project_id: self.project_id().to_string(),
            namespace_id: ns.map(|ns| ns.to_string()),
        };

        let path = PathElement {
            kind: kind.to_string(),
            name: name.map(|name| name.to_string()),
            id: id.map(|id| id.to_string()),
        };

        Key {
            path: vec![path],
            partition_id: Some(partition_id),
        }
    }
}
<|endoftext|>


<commit_before>//! Provides a way to create static/programmatically generated AWS Credentials.
//! For those who can't get them from an environment, or a file.

use chrono::{Duration, Utc};
use futures::future::{FutureResult, ok};

use {AwsCredentials, CredentialsError, ProvideAwsCredentials};

/// Provides AWS credentials from statically/programmatically provided strings.
#[derive(Clone, Debug)]
pub struct StaticProvider {
    /// The AWS Access Key ID to use for authenticating to AWS.
    aws_access_key_id: String,
    /// The AWS Secret Access Key to use for authenticating to AWS.
    aws_secret_access_key: String,
    /// The optional token to use for authenticating to AWS.
    token: Option<String>,
    /// The time in seconds each issued token should be valid for.
    valid_for: Option<i64>,
}

impl StaticProvider {
    /// Creates a new Static Provider. This should be used when you want to statically, or programmatically
    /// provide access to AWS.
    pub fn new(
        access_key: String,
        secret_access_key: String,
        token: Option<String>,
        valid_for: Option<i64>,
    ) -> StaticProvider {
        StaticProvider {
            aws_access_key_id: access_key,
            aws_secret_access_key: secret_access_key,
            token: token,
            valid_for: valid_for,
        }
    }

    /// Creates a new minimal Static Provider. This will set the token as optional none.
    pub fn new_minimal(access_key: String, secret_access_key: String) -> StaticProvider {
        StaticProvider {
            aws_access_key_id: access_key,
            aws_secret_access_key: secret_access_key,
            token: None,
            valid_for: None,
        }
    }

    /// Gets the AWS Access Key ID for this Static Provider.
    pub fn get_aws_access_key_id(&self) -> &str {
        &self.aws_access_key_id
    }

    /// Gets the AWS Secret Access Key for this Static Provider.
    pub fn get_aws_secret_access_key(&self) -> &str {
        &self.aws_secret_access_key
    }

    /// Determines if this Static Provider was given a Token.
    pub fn has_token(&self) -> bool {
        self.token.is_some()
    }

    /// Gets The Token this Static Provider was given.
    pub fn get_token(&self) -> &Option<String> {
        &self.token
    }

    /// Returns the length in seconds this Static Provider will be valid for.
    pub fn is_valid_for(&self) -> &Option<i64> {
        &self.valid_for
    }
}

impl ProvideAwsCredentials for StaticProvider {
    type Future = FutureResult<AwsCredentials, CredentialsError>;

    fn credentials(&self) -> Self::Future {
        ok(AwsCredentials::new(
            self.aws_access_key_id.clone(),
            self.aws_secret_access_key.clone(),
            self.token.clone(),
            self.valid_for.map(|v| Utc::now() + Duration::seconds(v)),
        ))
    }
}

#[cfg(test)]
mod tests {
    use futures::Future;

    use ProvideAwsCredentials;
    use super::*;

    #[test]
    fn test_static_provider_creation() {
        let result = StaticProvider::new(
            "fake-key".to_owned(),
            "fake-secret".to_owned(),
            Some("token".to_owned()),
            Some(300),
        ).credentials().wait();
        assert!(result.is_ok());
    }

    #[test]
    fn test_static_provider_minimal_creation() {
        let result =
            StaticProvider::new_minimal("fake-key-2".to_owned(), "fake-secret-2".to_owned())
                .credentials().wait();
        assert!(result.is_ok());
    }

    #[test]
    fn test_static_provider_custom_time_expiration() {
        let start_time = Utc::now();
        let result = StaticProvider::new(
            "fake-key".to_owned(),
            "fake-secret".to_owned(),
            None,
            Some(10000),
        ).credentials().wait();
        assert!(result.is_ok());
        let finalized = result.unwrap();
        let expires_at = finalized.expires_at().unwrap().clone();

        // Give a wide range of time, just incase there's somehow an immense amount of lag.
        assert!(start_time + Duration::minutes(100) < expires_at);
        assert!(expires_at < start_time + Duration::minutes(200));
    }
}
<commit_msg>Hide secrets in Debug impl of StaticProvider too<commit_after>//! Provides a way to create static/programmatically generated AWS Credentials.
//! For those who can't get them from an environment, or a file.

use chrono::{Duration, Utc};
use futures::future::{FutureResult, ok};

use {AwsCredentials, CredentialsError, ProvideAwsCredentials};

/// Provides AWS credentials from statically/programmatically provided strings.
#[derive(Clone, Debug)]
pub struct StaticProvider {
    /// AWS credentials
    credentials: AwsCredentials,

    /// The time in seconds for which each issued token is valid.
    valid_for: Option<i64>,
}

impl StaticProvider {
    /// Creates a new Static Provider. This should be used when you want to statically, or programmatically
    /// provide access to AWS.
    ///
    /// `valid_for` is the number of seconds for which issued tokens are valid.
    pub fn new(
        access_key: String,
        secret_access_key: String,
        token: Option<String>,
        valid_for: Option<i64>,
    ) -> StaticProvider {
        StaticProvider {
            credentials: AwsCredentials::new(
                access_key,
                secret_access_key,
                token,
                None
            ),
            valid_for: valid_for,
        }
    }

    /// Creates a new minimal Static Provider. This will set the token as optional none.
    pub fn new_minimal(access_key: String, secret_access_key: String) -> StaticProvider {
        StaticProvider {
            credentials: AwsCredentials::new(
                access_key,
                secret_access_key,
                None,
                None
            ),
            valid_for: None,
        }
    }

    /// Gets the AWS Access Key ID for this Static Provider.
    pub fn get_aws_access_key_id(&self) -> &str {
        &self.credentials.key
    }

    /// Gets the AWS Secret Access Key for this Static Provider.
    pub fn get_aws_secret_access_key(&self) -> &str {
        &self.credentials.secret
    }

    /// Determines if this Static Provider was given a Token.
    pub fn has_token(&self) -> bool {
        self.credentials.token.is_some()
    }

    /// Gets The Token this Static Provider was given.
    pub fn get_token(&self) -> &Option<String> {
        &self.credentials.token
    }

    /// Returns the length in seconds this Static Provider will be valid for.
    pub fn is_valid_for(&self) -> &Option<i64> {
        &self.valid_for
    }
}

impl ProvideAwsCredentials for StaticProvider {
    type Future = FutureResult<AwsCredentials, CredentialsError>;

    fn credentials(&self) -> Self::Future {
        let mut creds = self.credentials.clone();
        creds.expires_at = self.valid_for.map(|v| Utc::now() + Duration::seconds(v));
        ok(creds)
    }
}

#[cfg(test)]
mod tests {
    use futures::Future;
    use std::thread;
    use std::time;

    use ProvideAwsCredentials;
    use super::*;

    #[test]
    fn test_static_provider_creation() {
        let result = StaticProvider::new(
            "fake-key".to_owned(),
            "fake-secret".to_owned(),
            Some("token".to_owned()),
            Some(300),
        ).credentials().wait();
        assert!(result.is_ok());
    }

    #[test]
    fn test_static_provider_minimal_creation() {
        let result =
            StaticProvider::new_minimal("fake-key-2".to_owned(), "fake-secret-2".to_owned())
                .credentials().wait();
        assert!(result.is_ok());
    }

    #[test]
    fn test_static_provider_custom_time_expiration() {
        let start_time = Utc::now();
        let result = StaticProvider::new(
            "fake-key".to_owned(),
            "fake-secret".to_owned(),
            None,
            Some(10000),
        ).credentials().wait();
        assert!(result.is_ok());
        let finalized = result.unwrap();
        let expires_at = finalized.expires_at().unwrap().clone();

        // Give a wide range of time, just in case there's somehow an immense amount of lag.
        assert!(start_time + Duration::minutes(100) < expires_at);
        assert!(expires_at < start_time + Duration::minutes(200));
    }

    #[test]
    fn test_static_provider_expiration_time_is_recalculated() {
        let provider = StaticProvider::new(
            "fake-key".to_owned(),
            "fake-secret".to_owned(),
            None,
            Some(10000),
        );
        let creds1 = provider.credentials().wait().unwrap();
        thread::sleep(time::Duration::from_secs(1));
        let creds2 = provider.credentials().wait().unwrap();
        assert!(creds1.expires_at() < creds2.expires_at());
    }
}
<|endoftext|>


<commit_before>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use crate::*;
use async_graphql::connection::*;
use duniter_dbs::{bc_v2::BcV2DbReadable, GvaV1DbReadable};
use duniter_gva_dbs_reader::{uds_of_pubkey::UdsWithSum, PagedData};

#[derive(Default)]
pub(crate) struct UdsQuery;
#[async_graphql::Object]
impl UdsQuery {
    /// Current universal dividends amount
    async fn current_ud(
        &self,
        ctx: &async_graphql::Context<'_>,
    ) -> async_graphql::Result<Option<CurrentUdGva>> {
        let data = ctx.data::<SchemaData>()?;
        let dbs_reader = data.dbs_reader();

        Ok(data
            .dbs_pool
            .execute(move |dbs| dbs_reader.get_current_ud(&dbs.bc_db))
            .await??
            .map(|sa| CurrentUdGva {
                amount: sa.amount(),
                base: sa.base(),
            }))
    }
    /// Universal dividends issued by a public key
    #[allow(clippy::clippy::too_many_arguments)]
    async fn uds_of_pubkey(
        &self,
        ctx: &async_graphql::Context<'_>,
        #[graphql(desc = "Ed25519 public key on base 58 representation")] pubkey: String,
        #[graphql(default)] filter: UdsFilter,
        #[graphql(desc = "pagination", default)] pagination: Pagination,
        #[graphql(desc = "Amount needed")] amount: Option<i64>,
    ) -> async_graphql::Result<Connection<usize, UdGva, AggregateSum, EmptyFields>> {
        let pagination = Pagination::convert_to_page_info(pagination)?;

        let pubkey = PublicKey::from_base58(&pubkey)?;

        let data = ctx.data::<SchemaData>()?;
        //let dbs_reader = data.dbs_reader();

        let (
            PagedData {
                data: UdsWithSum { uds, sum },
                has_previous_page,
                has_next_page,
            },
            times,
        ) = data
            .dbs_pool
            .execute(move |dbs| {
                if let Some(current_block) =
                    duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)?
                {
                    let paged_data = match filter {
                        UdsFilter::All => duniter_gva_dbs_reader::uds_of_pubkey::all_uds_of_pubkey(
                            &dbs.bc_db,
                            &dbs.gva_db,
                            pubkey,
                            pagination,
                        ),
                        UdsFilter::Unspent => {
                            duniter_gva_dbs_reader::uds_of_pubkey::unspent_uds_of_pubkey(
                                &dbs.bc_db,
                                pubkey,
                                pagination,
                                None,
                                amount.map(|amount| {
                                    SourceAmount::new(amount, current_block.unit_base as i64)
                                }),
                            )
                        }
                    }?;
                    let mut times = Vec::with_capacity(paged_data.data.uds.len());
                    for (bn, _sa) in &paged_data.data.uds {
                        times.push(
                            dbs.gva_db
                                .blockchain_time()
                                .get(&U32BE(bn.0))?
                                .unwrap_or_else(|| unreachable!()),
                        );
                    }
                    Ok::<_, anyhow::Error>((paged_data, times))
                } else {
                    Err(anyhow::Error::msg("no blockchain"))
                }
            })
            .await??;

        let mut conn = Connection::with_additional_fields(
            has_previous_page,
            has_next_page,
            AggregateSum {
                aggregate: Sum {
                    sum: AmountWithBase {
                        amount: sum.amount() as i32,
                        base: sum.base() as i32,
                    },
                },
            },
        );
        let uds_timed =
            uds.into_iter()
                .zip(times.into_iter())
                .map(|((bn, sa), blockchain_time)| {
                    Edge::new(
                        bn.0 as usize,
                        UdGva {
                            amount: sa.amount(),
                            base: sa.base(),
                            issuer: pubkey.to_string(),
                            block_number: bn.0,
                            blockchain_time,
                        },
                    )
                });
        if pagination.order() {
            conn.append(uds_timed);
        } else {
            conn.append(uds_timed.rev());
        }
        Ok(conn)
    }
    /// Universal dividends revaluations
    async fn uds_reval(
        &self,
        ctx: &async_graphql::Context<'_>,
    ) -> async_graphql::Result<Vec<RevalUdGva>> {
        let data = ctx.data::<SchemaData>()?;

        Ok(data
            .dbs_pool
            .execute(move |dbs| {
                dbs.bc_db.uds_reval().iter(.., |it| {
                    it.map_ok(|(block_number, sa)| RevalUdGva {
                        amount: sa.0.amount(),
                        base: sa.0.base(),
                        block_number: block_number.0,
                    })
                    .collect::<KvResult<Vec<_>>>()
                })
            })
            .await??)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::*;

    #[tokio::test]
    async fn query_current_ud() -> anyhow::Result<()> {
        let mut dbs_reader = MockDbsReader::new();
        use duniter_dbs::bc_v2::BcV2Db;
        dbs_reader
            .expect_get_current_ud::<BcV2Db<FileBackend>>()
            .times(1)
            .returning(|_| Ok(Some(SourceAmount::with_base0(100))));
        let schema = create_schema(dbs_reader)?;
        assert_eq!(
            exec_graphql_request(&schema, r#"{ currentUd {amount} }"#).await?,
            serde_json::json!({
                "data": {
                    "currentUd": {
                      "amount": 100
                    }
                }
            })
        );
        Ok(())
    }
}
<commit_msg>[fix] gva: all cursors must be String<commit_after>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use crate::*;
use async_graphql::connection::*;
use duniter_dbs::{bc_v2::BcV2DbReadable, GvaV1DbReadable};
use duniter_gva_dbs_reader::{uds_of_pubkey::UdsWithSum, PagedData};

#[derive(Default)]
pub(crate) struct UdsQuery;
#[async_graphql::Object]
impl UdsQuery {
    /// Current universal dividends amount
    async fn current_ud(
        &self,
        ctx: &async_graphql::Context<'_>,
    ) -> async_graphql::Result<Option<CurrentUdGva>> {
        let data = ctx.data::<SchemaData>()?;
        let dbs_reader = data.dbs_reader();

        Ok(data
            .dbs_pool
            .execute(move |dbs| dbs_reader.get_current_ud(&dbs.bc_db))
            .await??
            .map(|sa| CurrentUdGva {
                amount: sa.amount(),
                base: sa.base(),
            }))
    }
    /// Universal dividends issued by a public key
    #[allow(clippy::clippy::too_many_arguments)]
    async fn uds_of_pubkey(
        &self,
        ctx: &async_graphql::Context<'_>,
        #[graphql(desc = "Ed25519 public key on base 58 representation")] pubkey: String,
        #[graphql(default)] filter: UdsFilter,
        #[graphql(desc = "pagination", default)] pagination: Pagination,
        #[graphql(desc = "Amount needed")] amount: Option<i64>,
    ) -> async_graphql::Result<Connection<String, UdGva, AggregateSum, EmptyFields>> {
        let pagination = Pagination::convert_to_page_info(pagination)?;

        let pubkey = PublicKey::from_base58(&pubkey)?;

        let data = ctx.data::<SchemaData>()?;
        //let dbs_reader = data.dbs_reader();

        let (
            PagedData {
                data: UdsWithSum { uds, sum },
                has_previous_page,
                has_next_page,
            },
            times,
        ) = data
            .dbs_pool
            .execute(move |dbs| {
                if let Some(current_block) =
                    duniter_dbs_read_ops::get_current_block_meta(&dbs.bc_db)?
                {
                    let paged_data = match filter {
                        UdsFilter::All => duniter_gva_dbs_reader::uds_of_pubkey::all_uds_of_pubkey(
                            &dbs.bc_db,
                            &dbs.gva_db,
                            pubkey,
                            pagination,
                        ),
                        UdsFilter::Unspent => {
                            duniter_gva_dbs_reader::uds_of_pubkey::unspent_uds_of_pubkey(
                                &dbs.bc_db,
                                pubkey,
                                pagination,
                                None,
                                amount.map(|amount| {
                                    SourceAmount::new(amount, current_block.unit_base as i64)
                                }),
                            )
                        }
                    }?;
                    let mut times = Vec::with_capacity(paged_data.data.uds.len());
                    for (bn, _sa) in &paged_data.data.uds {
                        times.push(
                            dbs.gva_db
                                .blockchain_time()
                                .get(&U32BE(bn.0))?
                                .unwrap_or_else(|| unreachable!()),
                        );
                    }
                    Ok::<_, anyhow::Error>((paged_data, times))
                } else {
                    Err(anyhow::Error::msg("no blockchain"))
                }
            })
            .await??;

        let mut conn = Connection::with_additional_fields(
            has_previous_page,
            has_next_page,
            AggregateSum {
                aggregate: Sum {
                    sum: AmountWithBase {
                        amount: sum.amount() as i32,
                        base: sum.base() as i32,
                    },
                },
            },
        );
        let uds_timed =
            uds.into_iter()
                .zip(times.into_iter())
                .map(|((bn, sa), blockchain_time)| {
                    Edge::new(
                        bn.0.to_string(),
                        UdGva {
                            amount: sa.amount(),
                            base: sa.base(),
                            issuer: pubkey.to_string(),
                            block_number: bn.0,
                            blockchain_time,
                        },
                    )
                });
        if pagination.order() {
            conn.append(uds_timed);
        } else {
            conn.append(uds_timed.rev());
        }
        Ok(conn)
    }
    /// Universal dividends revaluations
    async fn uds_reval(
        &self,
        ctx: &async_graphql::Context<'_>,
    ) -> async_graphql::Result<Vec<RevalUdGva>> {
        let data = ctx.data::<SchemaData>()?;

        Ok(data
            .dbs_pool
            .execute(move |dbs| {
                dbs.bc_db.uds_reval().iter(.., |it| {
                    it.map_ok(|(block_number, sa)| RevalUdGva {
                        amount: sa.0.amount(),
                        base: sa.0.base(),
                        block_number: block_number.0,
                    })
                    .collect::<KvResult<Vec<_>>>()
                })
            })
            .await??)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::*;

    #[tokio::test]
    async fn query_current_ud() -> anyhow::Result<()> {
        let mut dbs_reader = MockDbsReader::new();
        use duniter_dbs::bc_v2::BcV2Db;
        dbs_reader
            .expect_get_current_ud::<BcV2Db<FileBackend>>()
            .times(1)
            .returning(|_| Ok(Some(SourceAmount::with_base0(100))));
        let schema = create_schema(dbs_reader)?;
        assert_eq!(
            exec_graphql_request(&schema, r#"{ currentUd {amount} }"#).await?,
            serde_json::json!({
                "data": {
                    "currentUd": {
                      "amount": 100
                    }
                }
            })
        );
        Ok(())
    }
}
<|endoftext|>