<commit_before>use cairo;
use gio;
use glib;
use librsvg;
use rsvg_internals;

use gio::MemoryInputStreamExt;
use glib::Cast;

use librsvg::{CairoRenderer, Loader, SvgHandle};

use std::fs::File;
use std::io::BufReader;
use std::path::Path;

use self::rsvg_internals::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

mod compare_surfaces;

use crate::compare_surfaces::{compare_surfaces, BufferDiff};

fn load_svg(input: &'static [u8]) -> SvgHandle {
    let stream = gio::MemoryInputStream::new();
    stream.add_bytes(&glib::Bytes::from_static(input));

    Loader::new()
        .read_stream(&stream.upcast(), None, None)
        .unwrap()
}

#[test]
fn render_to_viewport_with_different_size() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
  <rect x="8" y="8" width="32" height="32" fill="blue"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let output = cairo::ImageSurface::create(cairo::Format::ARgb32, 128, 128).unwrap();

    {
        let cr = cairo::Context::new(&output);
        renderer
            .render_element_to_viewport(
                &cr,
                None,
                &cairo::Rectangle {
                    x: 0.0,
                    y: 0.0,
                    width: 128.0,
                    height: 128.0,
                },
            )
            .unwrap();
    }

    let output_surf = SharedImageSurface::new(output, SurfaceType::SRgb).unwrap();

    let fixture_path = Path::new("tests/fixtures/rect-48x48-rendered-128x128.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn render_to_offsetted_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
  <rect x="8" y="8" width="32" height="32" fill="blue"/>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let output = cairo::ImageSurface::create(cairo::Format::ARgb32, 100, 100).unwrap();

    {
        let cr = cairo::Context::new(&output);
        renderer
            .render_element_to_viewport(
                &cr,
                None,
                &cairo::Rectangle {
                    x: 10.0,
                    y: 20.0,
                    width: 48.0,
                    height: 48.0,
                },
            )
            .unwrap();
    }

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output.write_to_png(&mut output_file).unwrap();

    let output_surf = SharedImageSurface::new(output, SurfaceType::SRgb).unwrap();

    let fixture_path = Path::new("tests/fixtures/rect-48x48-offsetted-100x100-10x20.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn render_to_viewport_with_transform() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
  <g transform="translate(-10, -10)">
    <path fill="blue" d="M 18 18 l 32 0 l 0 32 l -32 0 z"/>
  </g>
</svg>
"#,
    );

    let renderer = CairoRenderer::new(&svg);

    let output = cairo::ImageSurface::create(cairo::Format::ARgb32, 100, 100).unwrap();

    {
        let cr = cairo::Context::new(&output);
        cr.translate(10.0, 20.0);
        renderer
            .render_element_to_viewport(
                &cr,
                None,
                &cairo::Rectangle {
                    x: 0.0,
                    y: 0.0,
                    width: 48.0,
                    height: 48.0,
                },
            )
            .unwrap();
    }

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output.write_to_png(&mut output_file).unwrap();

    let output_surf = SharedImageSurface::new(output, SurfaceType::SRgb).unwrap();

    let fixture_path = Path::new("tests/fixtures/rect-48x48-offsetted-100x100-10x20.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn clip_on_transformed_viewport() {
    let svg = load_svg(
        br##"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <defs>
    <clipPath id="one" clipPathUnits="objectBoundingBox">
      <circle cx="0.5" cy="0.5" r="0.5"/>
    </clipPath>
  </defs>
  <g clip-path="url(#one)">
    <rect x="10" y="10" width="40" height="40" fill="blue"/>
    <rect x="50" y="50" width="40" height="40" fill="limegreen"/>
  </g>
</svg>
"##,
    );

    let renderer = CairoRenderer::new(&svg);

    let output = cairo::ImageSurface::create(cairo::Format::ARgb32, 200, 200).unwrap();

    {
        let cr = cairo::Context::new(&output);
        cr.translate(50.0, 50.0);
        renderer
            .render_element_to_viewport(
                &cr,
                None,
                &cairo::Rectangle {
                    x: 0.0,
                    y: 0.0,
                    width: 100.0,
                    height: 100.0,
                },
            )
            .unwrap();
    }

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output.write_to_png(&mut output_file).unwrap();

    let output_surf = SharedImageSurface::new(output, SurfaceType::SRgb).unwrap();

    let fixture_path = Path::new("tests/fixtures/clip-on-transformed-viewport-200x200.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn mask_on_transformed_viewport() {
    let svg = load_svg(
        br##"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <defs>
    <mask id="one" maskContentUnits="objectBoundingBox">
      <circle cx="0.5" cy="0.5" r="0.5" fill="white"/>
    </mask>
  </defs>
  <g mask="url(#one)">
    <rect x="10" y="10" width="40" height="40" fill="blue"/>
    <rect x="50" y="50" width="40" height="40" fill="limegreen"/>
  </g>
</svg>
"##,
    );

    let renderer = CairoRenderer::new(&svg);

    let output = cairo::ImageSurface::create(cairo::Format::ARgb32, 200, 200).unwrap();

    {
        let cr = cairo::Context::new(&output);
        cr.translate(50.0, 50.0);
        renderer
            .render_element_to_viewport(
                &cr,
                None,
                &cairo::Rectangle {
                    x: 0.0,
                    y: 0.0,
                    width: 100.0,
                    height: 100.0,
                },
            )
            .unwrap();
    }

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output.write_to_png(&mut output_file).unwrap();

    let output_surf = SharedImageSurface::new(output, SurfaceType::SRgb).unwrap();

    let fixture_path = Path::new("tests/fixtures/mask-on-transformed-viewport-200x200.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}
<commit_msg>tests/render_to_viewport.rs: Extract function to do the rendering<commit_after>use cairo;
use gio;
use glib;
use librsvg;
use rsvg_internals;

use gio::MemoryInputStreamExt;
use glib::Cast;

use librsvg::{CairoRenderer, Loader, RenderingError, SvgHandle};

use std::fs::File;
use std::io::BufReader;
use std::path::Path;

use self::rsvg_internals::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

mod compare_surfaces;

use crate::compare_surfaces::{compare_surfaces, BufferDiff};

fn load_svg(input: &'static [u8]) -> SvgHandle {
    let stream = gio::MemoryInputStream::new();
    stream.add_bytes(&glib::Bytes::from_static(input));

    Loader::new()
        .read_stream(&stream.upcast(), None, None)
        .unwrap()
}

#[derive(Copy, Clone)]
struct SurfaceSize(i32, i32);

fn render_to_viewport<F: FnOnce(&cairo::Context)>(
    svg: &SvgHandle,
    surface_size: SurfaceSize,
    cr_transform: F,
    viewport: cairo::Rectangle,
) -> Result<SharedImageSurface, RenderingError> {
    let renderer = CairoRenderer::new(svg);

    let SurfaceSize(width, height) = surface_size;

    let output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height).unwrap();

    let res = {
        let cr = cairo::Context::new(&output);
        cr_transform(&cr);
        Ok(renderer.render_element_to_viewport(&cr, None, &viewport)?)
    };

    res.and_then(|_| Ok(SharedImageSurface::new(output, SurfaceType::SRgb)?))
}

#[test]
fn render_to_viewport_with_different_size() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
  <rect x="8" y="8" width="32" height="32" fill="blue"/>
</svg>
"#,
    );

    let output_surf = render_to_viewport(
        &svg,
        SurfaceSize(128, 128),
        |_cr| (),
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: 128.0,
            height: 128.0,
        },
    ).unwrap();

    let fixture_path = Path::new("tests/fixtures/rect-48x48-rendered-128x128.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn render_to_offsetted_viewport() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
  <rect x="8" y="8" width="32" height="32" fill="blue"/>
</svg>
"#,
    );

    let output_surf = render_to_viewport(
        &svg,
        SurfaceSize(100, 100),
        |_cr| (),
        cairo::Rectangle {
            x: 10.0,
            y: 20.0,
            width: 48.0,
            height: 48.0,
        },
    ).unwrap();

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output_surf.clone().into_image_surface().unwrap().write_to_png(&mut output_file).unwrap();

    let fixture_path = Path::new("tests/fixtures/rect-48x48-offsetted-100x100-10x20.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn render_to_viewport_with_transform() {
    let svg = load_svg(
        br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
  <g transform="translate(-10, -10)">
    <path fill="blue" d="M 18 18 l 32 0 l 0 32 l -32 0 z"/>
  </g>
</svg>
"#,
    );

    let output_surf = render_to_viewport(
        &svg,
        SurfaceSize(100, 100),
        |cr| cr.translate(10.0, 20.0),
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: 48.0,
            height: 48.0,
        },
    ).unwrap();

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output_surf.clone().into_image_surface().unwrap().write_to_png(&mut output_file).unwrap();

    let fixture_path = Path::new("tests/fixtures/rect-48x48-offsetted-100x100-10x20.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn clip_on_transformed_viewport() {
    let svg = load_svg(
        br##"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <defs>
    <clipPath id="one" clipPathUnits="objectBoundingBox">
      <circle cx="0.5" cy="0.5" r="0.5"/>
    </clipPath>
  </defs>
  <g clip-path="url(#one)">
    <rect x="10" y="10" width="40" height="40" fill="blue"/>
    <rect x="50" y="50" width="40" height="40" fill="limegreen"/>
  </g>
</svg>
"##,
    );

    let output_surf = render_to_viewport(
        &svg,
        SurfaceSize(200, 200),
        |cr| cr.translate(50.0, 50.0),
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: 100.0,
            height: 100.0,
        },
    ).unwrap();

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output_surf.clone().into_image_surface().unwrap().write_to_png(&mut output_file).unwrap();

    let fixture_path = Path::new("tests/fixtures/clip-on-transformed-viewport-200x200.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}

#[test]
fn mask_on_transformed_viewport() {
    let svg = load_svg(
        br##"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
  <defs>
    <mask id="one" maskContentUnits="objectBoundingBox">
      <circle cx="0.5" cy="0.5" r="0.5" fill="white"/>
    </mask>
  </defs>
  <g mask="url(#one)">
    <rect x="10" y="10" width="40" height="40" fill="blue"/>
    <rect x="50" y="50" width="40" height="40" fill="limegreen"/>
  </g>
</svg>
"##,
    );

    let output_surf = render_to_viewport(
        &svg,
        SurfaceSize(200, 200),
        |cr| cr.translate(50.0, 50.0),
        cairo::Rectangle {
            x: 0.0,
            y: 0.0,
            width: 100.0,
            height: 100.0,
        },
    ).unwrap();

    let mut output_file = File::create(Path::new("output.png")).unwrap();
    output_surf.clone().into_image_surface().unwrap().write_to_png(&mut output_file).unwrap();

    let fixture_path = Path::new("tests/fixtures/mask-on-transformed-viewport-200x200.png");
    let mut fixture_file = BufReader::new(File::open(fixture_path).unwrap());

    let expected = cairo::ImageSurface::create_from_png(&mut fixture_file).unwrap();
    let expected_surf = SharedImageSurface::new(expected, SurfaceType::SRgb).unwrap();

    let diff = compare_surfaces(&output_surf, &expected_surf).unwrap();

    match diff {
        BufferDiff::DifferentSizes => unreachable!("surfaces should be of the same size"),

        BufferDiff::Diff(diff) => {
            let surf = diff.surface.into_image_surface().unwrap();
            let mut output_file = File::create(Path::new("diff.png")).unwrap();
            surf.write_to_png(&mut output_file).unwrap();

            assert_eq!(diff.num_pixels_changed, 0);
        }
    }
}
<|endoftext|>


<commit_before>// crypto_verify_16.h

extern {
    pub fn crypto_verify_16(x: *const u8, y: *const u8) -> c_int;
}
<commit_msg>Completes definitions from crypto_verify_16.h<commit_after>// crypto_verify_16.h

pub const crypto_verify_16_BYTES: usize = 16;

extern {
    pub fn crypto_verify_16_bytes() -> size_t;
    pub fn crypto_verify_16(
        x: *const [u8; crypto_verify_16_BYTES],
        y: *const [u8; crypto_verify_16_BYTES])
        -> c_int;
}

#[test]
fn test_crypto_verify_16_bytes() {
   assert_eq!(unsafe { crypto_verify_16_bytes() as usize },
                       crypto_verify_16_BYTES);
}
<|endoftext|>


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

use std::{fs,
          path::Path,
          str::FromStr};

use super::{ExecutionStrategy,
            Scope};
use crate::{common::{package_graph::PackageGraph,
                     ui::{Status,
                          UIWriter,
                          UI}},
            error::{Error,
                    Result},
            hcore::{error as herror,
                    fs as hfs,
                    package::{Identifiable,
                              PackageIdent,
                              PackageInstall}}};

use crate::hcore::package::list::temp_package_directory;

/// Delete a package and all dependencies which are not used by other packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find the fully qualified package ident and all its dependencies
/// 2. We find all packages on the filesystem and convert them into a graph
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `services` is a list of fully-qualified `PackageIdent`s which are currently
///    running in a supervisor out of the `fs_root_path`.
pub fn start(ui: &mut UI,
             ident: &PackageIdent,
             fs_root_path: &Path,
             execution_strategy: ExecutionStrategy,
             scope: Scope,
             excludes: &[PackageIdent],
             services: &[PackageIdent])
             -> Result<()> {
    // 1.
    let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
    let ident = pkg_install.ident();
    ui.begin(format!("Uninstalling {}", &ident))?;

    if !services.is_empty() {
        ui.status(Status::Determining,
                  "list of running services in supervisor")?;
        for s in services.iter() {
            ui.status(Status::Found, format!("running service {}", s))?;
        }
    }

    // 2.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    // 3.
    let deps = graph.owned_ordered_deps(&ident);

    // 4.
    match graph.count_rdeps(&ident) {
        None => {
            // package not in graph - this shouldn't happen but could be a race condition in Step 2
            // with another hab uninstall. We can continue as what we wanted (package to
            // be removed) has already happened. We're going to continue and try and delete down
            // through the dependency tree anyway
            ui.warn(format!("Tried to find dependant packages of {} but it wasn't in graph.  \
                             Maybe another uninstall command was run at the same time?",
                            &ident))?;
        }
        Some(0) => {
            maybe_delete(ui,
                         &fs_root_path,
                         &pkg_install,
                         execution_strategy,
                         &excludes,
                         &services)?;
            graph.remove(&ident);
        }
        Some(c) => {
            return Err(Error::CannotRemovePackage(ident.clone(), c));
        }
    }

    // 5.
    let mut count = 0;
    match scope {
        Scope::Package => {
            ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
        }
        Scope::PackageAndDependencies => {
            for p in &deps {
                match graph.count_rdeps(&p) {
                    None => {
                        // package not in graph - this shouldn't happen but could be a race
                        // condition in Step 2 with another hab uninstall. We can
                        // continue as what we wanted (package to be removed) has already happened.
                        // We're going to continue and try and delete down through the
                        // dependency tree anyway
                        ui.warn(format!("Tried to find dependant packages of {} but it wasn't \
                                         in graph.  Maybe another uninstall command was run at \
                                         the same time?",
                                        &p))?;
                    }
                    Some(0) => {
                        let install = PackageInstall::load(&p, Some(fs_root_path))?;
                        maybe_delete(ui,
                                     &fs_root_path,
                                     &install,
                                     execution_strategy,
                                     &excludes,
                                     &services)?;

                        graph.remove(&p);
                        count += 1;
                    }
                    Some(c) => {
                        ui.status(Status::Skipping,
                                  format!("{}. It is a dependency of {} packages", &p, c))?;
                    }
                }
            }
        }
    }

    match execution_strategy {
        ExecutionStrategy::DryRun => {
            ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                           &ident, count))?;
        }
        ExecutionStrategy::Run => {
            ui.end(format!("Uninstall of {} and {} dependencies complete",
                           &ident, count))?;
        }
    };
    Ok(())
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete(ui: &mut UI,
                fs_root_path: &Path,
                install: &PackageInstall,
                strategy: ExecutionStrategy,
                excludes: &[PackageIdent],
                services: &[PackageIdent])
                -> Result<bool> {
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    let is_running = services.iter().any(|i| i.satisfies(ident));
    if is_running {
        ui.status(Status::Skipping,
                  format!("{}. It is currently running in the supervisor", &ident))?;
        return Ok(false);
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?;
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<commit_msg>uninstall should not rename to existing directory on windows<commit_after>// Copyright (c) 2018 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::{ExecutionStrategy,
            Scope};
use crate::error::{Error,
                   Result};
use habitat_common::{package_graph::PackageGraph,
                     ui::{Status,
                          UIWriter,
                          UI}};
use habitat_core::{error as herror,
                   fs as hfs,
                   package::{list::temp_package_directory,
                             Identifiable,
                             PackageIdent,
                             PackageInstall}};
use std::{fs,
          path::Path,
          str::FromStr};

/// Delete a package and all dependencies which are not used by other packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find the fully qualified package ident and all its dependencies
/// 2. We find all packages on the filesystem and convert them into a graph
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `services` is a list of fully-qualified `PackageIdent`s which are currently
///    running in a supervisor out of the `fs_root_path`.
pub fn start(ui: &mut UI,
             ident: &PackageIdent,
             fs_root_path: &Path,
             execution_strategy: ExecutionStrategy,
             scope: Scope,
             excludes: &[PackageIdent],
             services: &[PackageIdent])
             -> Result<()> {
    // 1.
    let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
    let ident = pkg_install.ident();
    ui.begin(format!("Uninstalling {}", &ident))?;

    if !services.is_empty() {
        ui.status(Status::Determining,
                  "list of running services in supervisor")?;
        for s in services.iter() {
            ui.status(Status::Found, format!("running service {}", s))?;
        }
    }

    // 2.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    // 3.
    let deps = graph.owned_ordered_deps(&ident);

    // 4.
    match graph.count_rdeps(&ident) {
        None => {
            // package not in graph - this shouldn't happen but could be a race condition in Step 2
            // with another hab uninstall. We can continue as what we wanted (package to
            // be removed) has already happened. We're going to continue and try and delete down
            // through the dependency tree anyway
            ui.warn(format!("Tried to find dependant packages of {} but it wasn't in graph.  \
                             Maybe another uninstall command was run at the same time?",
                            &ident))?;
        }
        Some(0) => {
            maybe_delete(ui,
                         &fs_root_path,
                         &pkg_install,
                         execution_strategy,
                         &excludes,
                         &services)?;
            graph.remove(&ident);
        }
        Some(c) => {
            return Err(Error::CannotRemovePackage(ident.clone(), c));
        }
    }

    // 5.
    let mut count = 0;
    match scope {
        Scope::Package => {
            ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
        }
        Scope::PackageAndDependencies => {
            for p in &deps {
                match graph.count_rdeps(&p) {
                    None => {
                        // package not in graph - this shouldn't happen but could be a race
                        // condition in Step 2 with another hab uninstall. We can
                        // continue as what we wanted (package to be removed) has already happened.
                        // We're going to continue and try and delete down through the
                        // dependency tree anyway
                        ui.warn(format!("Tried to find dependant packages of {} but it wasn't \
                                         in graph.  Maybe another uninstall command was run at \
                                         the same time?",
                                        &p))?;
                    }
                    Some(0) => {
                        let install = PackageInstall::load(&p, Some(fs_root_path))?;
                        maybe_delete(ui,
                                     &fs_root_path,
                                     &install,
                                     execution_strategy,
                                     &excludes,
                                     &services)?;

                        graph.remove(&p);
                        count += 1;
                    }
                    Some(c) => {
                        ui.status(Status::Skipping,
                                  format!("{}. It is a dependency of {} packages", &p, c))?;
                    }
                }
            }
        }
    }

    match execution_strategy {
        ExecutionStrategy::DryRun => {
            ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                           &ident, count))?;
        }
        ExecutionStrategy::Run => {
            ui.end(format!("Uninstall of {} and {} dependencies complete",
                           &ident, count))?;
        }
    };
    Ok(())
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete(ui: &mut UI,
                fs_root_path: &Path,
                install: &PackageInstall,
                strategy: ExecutionStrategy,
                excludes: &[PackageIdent],
                services: &[PackageIdent])
                -> Result<bool> {
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    let is_running = services.iter().any(|i| i.satisfies(ident));
    if is_running {
        ui.status(Status::Skipping,
                  format!("{}. It is currently running in the supervisor", &ident))?;
        return Ok(false);
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?.path()
                                                                              .to_path_buf();
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<|endoftext|>


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

<%! from data import Keyword, to_rust_ident, to_camel_case %>

<%def name="longhand(name, **kwargs)">
    <%call expr="raw_longhand(name, **kwargs)">
        ${caller.body()}
        % if not data.longhands_by_name[name].derived_from:
            pub fn parse_specified(context: &ParserContext, input: &mut Parser)
                               -> Result<DeclaredValue<SpecifiedValue>, ()> {
                parse(context, input).map(DeclaredValue::Value)
            }
        % endif
    </%call>
</%def>

<%def name="predefined_type(name, type, initial_value, parse_method='parse', **kwargs)">
    <%call expr="longhand(name, predefined_type=type, **kwargs)">
        #[allow(unused_imports)]
        use app_units::Au;
        use cssparser::{Color as CSSParserColor, RGBA};
        pub type SpecifiedValue = specified::${type};
        pub mod computed_value {
            pub use values::computed::${type} as T;
        }
        #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
        #[inline] pub fn parse(_context: &ParserContext, input: &mut Parser)
                               -> Result<SpecifiedValue, ()> {
            specified::${type}::${parse_method}(input)
        }
    </%call>
</%def>

<%def name="raw_longhand(*args, **kwargs)">
    <%
        property = data.declare_longhand(*args, **kwargs)
        if property is None:
            return ""
    %>
    pub mod ${property.ident} {
        #![allow(unused_imports)]
        % if not property.derived_from:
            use cssparser::Parser;
            use parser::{ParserContext, ParserContextExtraData};
            use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
        % endif
        use error_reporting::ParseErrorReporter;
        use properties::longhands;
        use properties::property_bit_field::PropertyBitField;
        use properties::{ComputedValues, ServoComputedValues, PropertyDeclaration};
        use properties::style_struct_traits::${data.current_style_struct.trait_name};
        use properties::style_structs;
        use std::boxed::Box as StdBox;
        use std::collections::HashMap;
        use std::sync::Arc;
        use values::computed::{TContext, ToComputedValue};
        use values::{computed, specified};
        use string_cache::Atom;
        ${caller.body()}
        #[allow(unused_variables)]
        pub fn cascade_property<C: ComputedValues>(
                                declaration: &PropertyDeclaration,
                                inherited_style: &C,
                                context: &mut computed::Context<C>,
                                seen: &mut PropertyBitField,
                                cacheable: &mut bool,
                                error_reporter: &mut StdBox<ParseErrorReporter + Send>) {
            let declared_value = match *declaration {
                PropertyDeclaration::${property.camel_case}(ref declared_value) => {
                    declared_value
                }
                _ => panic!("entered the wrong cascade_property() implementation"),
            };
            % if not property.derived_from:
                if seen.get_${property.ident}() {
                    return
                }
                seen.set_${property.ident}();
                {
                    let custom_props = context.style().custom_properties();
                    ::properties::substitute_variables_${property.ident}(
                        declared_value, &custom_props, |value| match *value {
                            DeclaredValue::Value(ref specified_value) => {
                                let computed = specified_value.to_computed_value(context);
                                context.mutate_style().mutate_${data.current_style_struct.trait_name_lower}()
                                                      .set_${property.ident}(computed);
                            }
                            DeclaredValue::WithVariables { .. } => unreachable!(),
                            DeclaredValue::Initial => {
                                // We assume that it's faster to use copy_*_from rather than
                                // set_*(get_initial_value());
                                let initial_struct = C::initial_values()
                                                      .get_${data.current_style_struct.trait_name_lower}();
                                context.mutate_style().mutate_${data.current_style_struct.trait_name_lower}()
                                                      .copy_${property.ident}_from(initial_struct);
                            },
                            DeclaredValue::Inherit => {
                                // This is a bit slow, but this is rare so it shouldn't
                                // matter.
                                //
                                // FIXME: is it still?
                                *cacheable = false;
                                let inherited_struct =
                                    inherited_style.get_${data.current_style_struct.trait_name_lower}();
                                context.mutate_style().mutate_${data.current_style_struct.trait_name_lower}()
                                       .copy_${property.ident}_from(inherited_struct);
                            }
                        }, error_reporter
                    );
                }

                % if property.custom_cascade:
                    cascade_property_custom(declaration,
                                            inherited_style,
                                            context,
                                            seen,
                                            cacheable,
                                            error_reporter);
                % endif
            % else:
                // Do not allow stylesheets to set derived properties.
            % endif
        }
        % if not property.derived_from:
            pub fn parse_declared(context: &ParserContext, input: &mut Parser)
                               -> Result<DeclaredValue<SpecifiedValue>, ()> {
                match input.try(CSSWideKeyword::parse) {
                    Ok(CSSWideKeyword::InheritKeyword) => Ok(DeclaredValue::Inherit),
                    Ok(CSSWideKeyword::InitialKeyword) => Ok(DeclaredValue::Initial),
                    Ok(CSSWideKeyword::UnsetKeyword) => Ok(DeclaredValue::${
                        "Inherit" if data.current_style_struct.inherited else "Initial"}),
                    Err(()) => {
                        input.look_for_var_functions();
                        let start = input.position();
                        let specified = parse_specified(context, input);
                        if specified.is_err() {
                            while let Ok(_) = input.next() {}  // Look for var() after the error.
                        }
                        let var = input.seen_var_functions();
                        if specified.is_err() && var {
                            input.reset(start);
                            let (first_token_type, css) = try!(
                                ::custom_properties::parse_non_custom_with_var(input));
                            return Ok(DeclaredValue::WithVariables {
                                css: css.into_owned(),
                                first_token_type: first_token_type,
                                base_url: context.base_url.clone(),
                                from_shorthand: None,
                            })
                        }
                        specified
                    }
                }
            }
        % endif
    }
</%def>

<%def name="single_keyword(name, values, **kwargs)">
    <%call expr="single_keyword_computed(name, values, **kwargs)">
        use values::computed::ComputedValueAsSpecified;
        impl ComputedValueAsSpecified for SpecifiedValue {}
    </%call>
</%def>

<%def name="single_keyword_computed(name, values, **kwargs)">
    <%
        keyword_kwargs = {a: kwargs.pop(a, None) for a in [
            'gecko_constant_prefix', 'extra_gecko_values', 'extra_servo_values'
        ]}
    %>
    <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
        pub use self::computed_value::T as SpecifiedValue;
        ${caller.body()}
        pub mod computed_value {
            define_css_keyword_enum! { T:
                % for value in data.longhands_by_name[name].keyword.values_for(product):
                    "${value}" => ${to_rust_ident(value)},
                % endfor
            }
        }
        #[inline]
        pub fn get_initial_value() -> computed_value::T {
            computed_value::T::${to_rust_ident(values.split()[0])}
        }
        #[inline]
        pub fn parse(_context: &ParserContext, input: &mut Parser)
                     -> Result<SpecifiedValue, ()> {
            computed_value::T::parse(input)
        }
    </%call>
</%def>

<%def name="keyword_list(name, values, **kwargs)">
    <%
        keyword_kwargs = {a: kwargs.pop(a, None) for a in [
            'gecko_constant_prefix', 'extra_gecko_values', 'extra_servo_values'
        ]}
    %>
    <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
        use values::computed::ComputedValueAsSpecified;
        pub use self::computed_value::T as SpecifiedValue;
        pub mod computed_value {
            use cssparser::ToCss;
            use std::fmt;

            #[derive(Debug, Clone, PartialEq)]
            #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
            pub struct T(pub Vec<${to_camel_case(name)}>);

            impl ToCss for T {
                fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                    debug_assert!(!self.0.is_empty(), "Always parses at least one");

                    for (index, item) in self.0.iter().enumerate() {
                        if index != 0 {
                            try!(dest.write_str(", "));
                        }

                        try!(item.to_css(dest));
                    }

                    Ok(())
                }
            }

            pub use self::${to_camel_case(name)} as SingleComputedValue;

            define_css_keyword_enum! { ${to_camel_case(name)}:
                % for value in data.longhands_by_name[name].keyword.values_for(product):
                    "${value}" => ${to_rust_ident(value)},
                % endfor
            }
        }

        pub use self::computed_value::${to_camel_case(name)} as SingleSpecifiedValue;

        #[inline]
        pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue, ()> {
            SingleSpecifiedValue::parse(input)
        }

        #[inline]
        pub fn get_initial_value() -> computed_value::T {
            computed_value::T(vec![get_initial_single_value()])
        }

        #[inline]
        pub fn get_initial_single_value() -> SingleSpecifiedValue {
            SingleSpecifiedValue::${to_rust_ident(values.split()[0])}
        }

        #[inline]
        pub fn parse(_context: &ParserContext, input: &mut Parser)
                     -> Result<SpecifiedValue, ()> {
            Ok(SpecifiedValue(try!(
                input.parse_comma_separated(computed_value::${to_camel_case(name)}::parse))))
        }

        impl ComputedValueAsSpecified for SpecifiedValue {}
    </%call>
</%def>

<%def name="shorthand(name, sub_properties, experimental=False, **kwargs)">
<%
    shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental,
                                       **kwargs)
%>
    % if shorthand:
    pub mod ${shorthand.ident} {
        use cssparser::Parser;
        use parser::ParserContext;
        use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand};

        pub struct Longhands {
            % for sub_property in shorthand.sub_properties:
                pub ${sub_property.ident}:
                    Option<longhands::${sub_property.ident}::SpecifiedValue>,
            % endfor
        }

        pub fn parse(context: &ParserContext, input: &mut Parser,
                     declarations: &mut Vec<PropertyDeclaration>)
                     -> Result<(), ()> {
            input.look_for_var_functions();
            let start = input.position();
            let value = input.parse_entirely(|input| parse_value(context, input));
            if value.is_err() {
                while let Ok(_) = input.next() {}  // Look for var() after the error.
            }
            let var = input.seen_var_functions();
            if let Ok(value) = value {
                % for sub_property in shorthand.sub_properties:
                    declarations.push(PropertyDeclaration::${sub_property.camel_case}(
                        match value.${sub_property.ident} {
                            Some(value) => DeclaredValue::Value(value),
                            None => DeclaredValue::Initial,
                        }
                    ));
                % endfor
                Ok(())
            } else if var {
                input.reset(start);
                let (first_token_type, css) = try!(
                    ::custom_properties::parse_non_custom_with_var(input));
                % for sub_property in shorthand.sub_properties:
                    declarations.push(PropertyDeclaration::${sub_property.camel_case}(
                        DeclaredValue::WithVariables {
                            css: css.clone().into_owned(),
                            first_token_type: first_token_type,
                            base_url: context.base_url.clone(),
                            from_shorthand: Some(Shorthand::${shorthand.camel_case}),
                        }
                    ));
                % endfor
                Ok(())
            } else {
                Err(())
            }
        }

        #[allow(unused_variables)]
        pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
            ${caller.body()}
        }
    }
    % endif
</%def>

<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)">
    <%self:shorthand name="${name}" sub_properties="${
            ' '.join(sub_property_pattern % side
                     for side in ['top', 'right', 'bottom', 'left'])}">
        use super::parse_four_sides;
        use values::specified;
        let _unused = context;
        let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function}));
        Ok(Longhands {
            % for side in ["top", "right", "bottom", "left"]:
                ${to_rust_ident(sub_property_pattern % side)}: Some(${side}),
            % endfor
        })
    </%self:shorthand>
</%def>
<commit_msg>Add framework for properties which are single elements in servo and arrays in gecko<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%! from data import Keyword, to_rust_ident, to_camel_case %>

<%def name="longhand(name, **kwargs)">
    <%call expr="raw_longhand(name, **kwargs)">
        ${caller.body()}
        % if not data.longhands_by_name[name].derived_from:
            pub fn parse_specified(context: &ParserContext, input: &mut Parser)
                               -> Result<DeclaredValue<SpecifiedValue>, ()> {
                parse(context, input).map(DeclaredValue::Value)
            }
        % endif
    </%call>
</%def>

<%def name="predefined_type(name, type, initial_value, parse_method='parse', **kwargs)">
    <%call expr="longhand(name, predefined_type=type, **kwargs)">
        #[allow(unused_imports)]
        use app_units::Au;
        use cssparser::{Color as CSSParserColor, RGBA};
        pub type SpecifiedValue = specified::${type};
        pub mod computed_value {
            pub use values::computed::${type} as T;
        }
        #[inline] pub fn get_initial_value() -> computed_value::T { ${initial_value} }
        #[inline] pub fn parse(_context: &ParserContext, input: &mut Parser)
                               -> Result<SpecifiedValue, ()> {
            specified::${type}::${parse_method}(input)
        }
    </%call>
</%def>

<%doc>
    To be used in cases where we have a grammar like
    "<thing> [ , <thing> ]*", but only support a single value
    in servo
</%doc>
<%def name="gecko_autoarray_longhand(name, **kwargs)">
    <%call expr="longhand(name, **kwargs)">
        % if product == "gecko":
            use cssparser::ToCss;
            use std::fmt;

            pub mod single_value {
                use cssparser::Parser;
                use parser::{ParserContext, ParserContextExtraData};
                use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
                use values::computed::{TContext, ToComputedValue};
                use values::{computed, specified};
                ${caller.body()}
            }
            pub mod computed_value {
                use super::single_value;
                #[derive(Debug, Clone, PartialEq, HeapSizeOf)]
                pub struct T(pub Vec<single_value::computed_value::T>);
            }

            impl ToCss for computed_value::T {
                fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                    if self.0.len() >= 1 {
                        try!(self.0[0].to_css(dest));
                    }
                    for i in self.0.iter().skip(1) {
                        try!(dest.write_str(", "));
                        try!(i.to_css(dest));
                    }
                    Ok(())
                }
            }

            #[derive(Debug, Clone, PartialEq, HeapSizeOf)]
            pub struct SpecifiedValue(pub Vec<single_value::SpecifiedValue>);

            impl ToCss for SpecifiedValue {
                fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                    if self.0.len() >= 1 {
                        try!(self.0[0].to_css(dest))
                    }
                    for i in self.0.iter().skip(1) {
                        try!(dest.write_str(", "));
                        try!(i.to_css(dest));
                    }
                    Ok(())
                }
            }
            pub fn get_initial_value() -> computed_value::T {
                computed_value::T(vec![single_value::get_initial_value()])
            }
            pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
                input.parse_comma_separated(|parser| {
                    single_value::parse(context, parser)
                }).map(|ok| SpecifiedValue(ok))
            }
            impl ToComputedValue for SpecifiedValue {
                type ComputedValue = computed_value::T;

                #[inline]
                fn to_computed_value<Cx: TContext>(&self, context: &Cx) -> computed_value::T {
                    computed_value::T(self.0.iter().map(|x| x.to_computed_value(context)).collect())
                }
            }
        % else:
            ${caller.body()}
        % endif
    </%call>
</%def>

<%def name="raw_longhand(*args, **kwargs)">
    <%
        property = data.declare_longhand(*args, **kwargs)
        if property is None:
            return ""
    %>
    pub mod ${property.ident} {
        #![allow(unused_imports)]
        % if not property.derived_from:
            use cssparser::Parser;
            use parser::{ParserContext, ParserContextExtraData};
            use properties::{CSSWideKeyword, DeclaredValue, Shorthand};
        % endif
        use error_reporting::ParseErrorReporter;
        use properties::longhands;
        use properties::property_bit_field::PropertyBitField;
        use properties::{ComputedValues, ServoComputedValues, PropertyDeclaration};
        use properties::style_struct_traits::${data.current_style_struct.trait_name};
        use properties::style_structs;
        use std::boxed::Box as StdBox;
        use std::collections::HashMap;
        use std::sync::Arc;
        use values::computed::{TContext, ToComputedValue};
        use values::{computed, specified};
        use string_cache::Atom;
        ${caller.body()}
        #[allow(unused_variables)]
        pub fn cascade_property<C: ComputedValues>(
                                declaration: &PropertyDeclaration,
                                inherited_style: &C,
                                context: &mut computed::Context<C>,
                                seen: &mut PropertyBitField,
                                cacheable: &mut bool,
                                error_reporter: &mut StdBox<ParseErrorReporter + Send>) {
            let declared_value = match *declaration {
                PropertyDeclaration::${property.camel_case}(ref declared_value) => {
                    declared_value
                }
                _ => panic!("entered the wrong cascade_property() implementation"),
            };
            % if not property.derived_from:
                if seen.get_${property.ident}() {
                    return
                }
                seen.set_${property.ident}();
                {
                    let custom_props = context.style().custom_properties();
                    ::properties::substitute_variables_${property.ident}(
                        declared_value, &custom_props, |value| match *value {
                            DeclaredValue::Value(ref specified_value) => {
                                let computed = specified_value.to_computed_value(context);
                                context.mutate_style().mutate_${data.current_style_struct.trait_name_lower}()
                                                      .set_${property.ident}(computed);
                            }
                            DeclaredValue::WithVariables { .. } => unreachable!(),
                            DeclaredValue::Initial => {
                                // We assume that it's faster to use copy_*_from rather than
                                // set_*(get_initial_value());
                                let initial_struct = C::initial_values()
                                                      .get_${data.current_style_struct.trait_name_lower}();
                                context.mutate_style().mutate_${data.current_style_struct.trait_name_lower}()
                                                      .copy_${property.ident}_from(initial_struct);
                            },
                            DeclaredValue::Inherit => {
                                // This is a bit slow, but this is rare so it shouldn't
                                // matter.
                                //
                                // FIXME: is it still?
                                *cacheable = false;
                                let inherited_struct =
                                    inherited_style.get_${data.current_style_struct.trait_name_lower}();
                                context.mutate_style().mutate_${data.current_style_struct.trait_name_lower}()
                                       .copy_${property.ident}_from(inherited_struct);
                            }
                        }, error_reporter
                    );
                }

                % if property.custom_cascade:
                    cascade_property_custom(declaration,
                                            inherited_style,
                                            context,
                                            seen,
                                            cacheable,
                                            error_reporter);
                % endif
            % else:
                // Do not allow stylesheets to set derived properties.
            % endif
        }
        % if not property.derived_from:
            pub fn parse_declared(context: &ParserContext, input: &mut Parser)
                               -> Result<DeclaredValue<SpecifiedValue>, ()> {
                match input.try(CSSWideKeyword::parse) {
                    Ok(CSSWideKeyword::InheritKeyword) => Ok(DeclaredValue::Inherit),
                    Ok(CSSWideKeyword::InitialKeyword) => Ok(DeclaredValue::Initial),
                    Ok(CSSWideKeyword::UnsetKeyword) => Ok(DeclaredValue::${
                        "Inherit" if data.current_style_struct.inherited else "Initial"}),
                    Err(()) => {
                        input.look_for_var_functions();
                        let start = input.position();
                        let specified = parse_specified(context, input);
                        if specified.is_err() {
                            while let Ok(_) = input.next() {}  // Look for var() after the error.
                        }
                        let var = input.seen_var_functions();
                        if specified.is_err() && var {
                            input.reset(start);
                            let (first_token_type, css) = try!(
                                ::custom_properties::parse_non_custom_with_var(input));
                            return Ok(DeclaredValue::WithVariables {
                                css: css.into_owned(),
                                first_token_type: first_token_type,
                                base_url: context.base_url.clone(),
                                from_shorthand: None,
                            })
                        }
                        specified
                    }
                }
            }
        % endif
    }
</%def>

<%def name="single_keyword(name, values, **kwargs)">
    <%call expr="single_keyword_computed(name, values, **kwargs)">
        use values::computed::ComputedValueAsSpecified;
        impl ComputedValueAsSpecified for SpecifiedValue {}
    </%call>
</%def>

<%def name="single_keyword_computed(name, values, **kwargs)">
    <%
        keyword_kwargs = {a: kwargs.pop(a, None) for a in [
            'gecko_constant_prefix', 'extra_gecko_values', 'extra_servo_values'
        ]}
    %>
    <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
        pub use self::computed_value::T as SpecifiedValue;
        ${caller.body()}
        pub mod computed_value {
            define_css_keyword_enum! { T:
                % for value in data.longhands_by_name[name].keyword.values_for(product):
                    "${value}" => ${to_rust_ident(value)},
                % endfor
            }
        }
        #[inline]
        pub fn get_initial_value() -> computed_value::T {
            computed_value::T::${to_rust_ident(values.split()[0])}
        }
        #[inline]
        pub fn parse(_context: &ParserContext, input: &mut Parser)
                     -> Result<SpecifiedValue, ()> {
            computed_value::T::parse(input)
        }
    </%call>
</%def>

<%def name="keyword_list(name, values, **kwargs)">
    <%
        keyword_kwargs = {a: kwargs.pop(a, None) for a in [
            'gecko_constant_prefix', 'extra_gecko_values', 'extra_servo_values'
        ]}
    %>
    <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
        use values::computed::ComputedValueAsSpecified;
        pub use self::computed_value::T as SpecifiedValue;
        pub mod computed_value {
            use cssparser::ToCss;
            use std::fmt;

            #[derive(Debug, Clone, PartialEq)]
            #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
            pub struct T(pub Vec<${to_camel_case(name)}>);

            impl ToCss for T {
                fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                    debug_assert!(!self.0.is_empty(), "Always parses at least one");

                    for (index, item) in self.0.iter().enumerate() {
                        if index != 0 {
                            try!(dest.write_str(", "));
                        }

                        try!(item.to_css(dest));
                    }

                    Ok(())
                }
            }

            pub use self::${to_camel_case(name)} as SingleComputedValue;

            define_css_keyword_enum! { ${to_camel_case(name)}:
                % for value in data.longhands_by_name[name].keyword.values_for(product):
                    "${value}" => ${to_rust_ident(value)},
                % endfor
            }
        }

        pub use self::computed_value::${to_camel_case(name)} as SingleSpecifiedValue;

        #[inline]
        pub fn parse_one(input: &mut Parser) -> Result<SingleSpecifiedValue, ()> {
            SingleSpecifiedValue::parse(input)
        }

        #[inline]
        pub fn get_initial_value() -> computed_value::T {
            computed_value::T(vec![get_initial_single_value()])
        }

        #[inline]
        pub fn get_initial_single_value() -> SingleSpecifiedValue {
            SingleSpecifiedValue::${to_rust_ident(values.split()[0])}
        }

        #[inline]
        pub fn parse(_context: &ParserContext, input: &mut Parser)
                     -> Result<SpecifiedValue, ()> {
            Ok(SpecifiedValue(try!(
                input.parse_comma_separated(computed_value::${to_camel_case(name)}::parse))))
        }

        impl ComputedValueAsSpecified for SpecifiedValue {}
    </%call>
</%def>

<%def name="shorthand(name, sub_properties, experimental=False, **kwargs)">
<%
    shorthand = data.declare_shorthand(name, sub_properties.split(), experimental=experimental,
                                       **kwargs)
%>
    % if shorthand:
    pub mod ${shorthand.ident} {
        use cssparser::Parser;
        use parser::ParserContext;
        use properties::{longhands, PropertyDeclaration, DeclaredValue, Shorthand};

        pub struct Longhands {
            % for sub_property in shorthand.sub_properties:
                pub ${sub_property.ident}:
                    Option<longhands::${sub_property.ident}::SpecifiedValue>,
            % endfor
        }

        pub fn parse(context: &ParserContext, input: &mut Parser,
                     declarations: &mut Vec<PropertyDeclaration>)
                     -> Result<(), ()> {
            input.look_for_var_functions();
            let start = input.position();
            let value = input.parse_entirely(|input| parse_value(context, input));
            if value.is_err() {
                while let Ok(_) = input.next() {}  // Look for var() after the error.
            }
            let var = input.seen_var_functions();
            if let Ok(value) = value {
                % for sub_property in shorthand.sub_properties:
                    declarations.push(PropertyDeclaration::${sub_property.camel_case}(
                        match value.${sub_property.ident} {
                            Some(value) => DeclaredValue::Value(value),
                            None => DeclaredValue::Initial,
                        }
                    ));
                % endfor
                Ok(())
            } else if var {
                input.reset(start);
                let (first_token_type, css) = try!(
                    ::custom_properties::parse_non_custom_with_var(input));
                % for sub_property in shorthand.sub_properties:
                    declarations.push(PropertyDeclaration::${sub_property.camel_case}(
                        DeclaredValue::WithVariables {
                            css: css.clone().into_owned(),
                            first_token_type: first_token_type,
                            base_url: context.base_url.clone(),
                            from_shorthand: Some(Shorthand::${shorthand.camel_case}),
                        }
                    ));
                % endfor
                Ok(())
            } else {
                Err(())
            }
        }

        #[allow(unused_variables)]
        pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
            ${caller.body()}
        }
    }
    % endif
</%def>

<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function)">
    <%self:shorthand name="${name}" sub_properties="${
            ' '.join(sub_property_pattern % side
                     for side in ['top', 'right', 'bottom', 'left'])}">
        use super::parse_four_sides;
        use values::specified;
        let _unused = context;
        let (top, right, bottom, left) = try!(parse_four_sides(input, ${parser_function}));
        Ok(Longhands {
            % for side in ["top", "right", "bottom", "left"]:
                ${to_rust_ident(sub_property_pattern % side)}: Some(${side}),
            % endfor
        })
    </%self:shorthand>
</%def>
<|endoftext|>


<commit_before>// This is a part of rust-encoding.
// Copyright (c) 2013-2014, Kang Seonghoon.
// See README.md and LICENSE.txt for details.

/*!

# Rust-encoding

Character encoding support for Rust.
It is based on [WHATWG Encoding Standard](http://encoding.spec.whatwg.org/),
and also provides an advanced interface for error detection and recovery.

## Simple Usage

To encode a string:

~~~~ {.rust}
use encoding::{Encoding, EncodeStrict};
use encoding::all::ISO_8859_1;

assert_eq!(ISO_8859_1.encode("caf\xe9", EncodeStrict),
           Ok(vec!(99,97,102,233)));
~~~~

To encode a string with unrepresentable characters:

~~~~ {.rust}
use encoding::{Encoding, EncodeStrict, EncodeReplace, EncodeIgnore, EncodeNcrEscape};
use encoding::all::ISO_8859_2;

assert!(ISO_8859_2.encode("Acme\xa9", EncodeStrict).is_err());
assert_eq!(ISO_8859_2.encode("Acme\xa9", EncodeReplace),
           Ok(vec!(65,99,109,101,63)));
assert_eq!(ISO_8859_2.encode("Acme\xa9", EncodeIgnore),
           Ok(vec!(65,99,109,101)));
assert_eq!(ISO_8859_2.encode("Acme\xa9", EncodeNcrEscape),
           Ok(vec!(65,99,109,101,38,35,49,54,57,59)));
~~~~

To decode a byte sequence:

~~~~ {.rust}
use encoding::{Encoding, DecodeStrict};
use encoding::all::ISO_8859_1;

assert_eq!(ISO_8859_1.decode([99,97,102,233], DecodeStrict),
           Ok("caf\xe9".to_string()));
~~~~

To decode a byte sequence with invalid sequences:

~~~~ {.rust}
use encoding::{Encoding, DecodeStrict, DecodeReplace, DecodeIgnore};
use encoding::all::ISO_8859_6;

assert!(ISO_8859_6.decode([65,99,109,101,169], DecodeStrict).is_err());
assert_eq!(ISO_8859_6.decode([65,99,109,101,169], DecodeReplace),
           Ok("Acme\ufffd".to_string()));
assert_eq!(ISO_8859_6.decode([65,99,109,101,169], DecodeIgnore),
           Ok("Acme".to_string()));
~~~~

A practical example of custom encoder traps:

~~~~ {.rust}
use encoding::{Encoding, Encoder, ByteWriter, EncoderTrap, EncoderCall, DecodeStrict};
use encoding::all::ASCII;

// hexadecimal numeric character reference replacement
fn hex_ncr_escape(_encoder: &mut Encoder, input: &str, output: &mut ByteWriter) -> bool {
    let escapes: Vec<String> =
        input.chars().map(|ch| format!("&#x{:x};", ch as int)).collect();
    let escapes = escapes.concat();
    output.write_bytes(escapes.as_bytes());
    true
}
static HexNcrEscape: EncoderTrap = EncoderCall(hex_ncr_escape);

let orig = "Hello, 世界!".to_string();
let encoded = ASCII.encode(orig[], HexNcrEscape).unwrap();
assert_eq!(ASCII.decode(encoded[], DecodeStrict),
           Ok("Hello, 世界!".to_string()));
~~~~

Getting the encoding from the string label, as specified in WHATWG Encoding standard:

~~~~ {.rust}
use encoding::{Encoding, DecodeReplace};
use encoding::label::encoding_from_whatwg_label;
use encoding::all::WINDOWS_949;

let euckr = encoding_from_whatwg_label("euc-kr").unwrap();
assert_eq!(euckr.name(), "windows-949");
assert_eq!(euckr.whatwg_name(), Some("euc-kr")); // for the sake of compatibility
let broken = &[0xbf, 0xec, 0xbf, 0xcd, 0xff, 0xbe, 0xd3];
assert_eq!(euckr.decode(broken, DecodeReplace),
           Ok("\uc6b0\uc640\ufffd\uc559".to_string()));

// corresponding rust-encoding native API:
assert_eq!(WINDOWS_949.decode(broken, DecodeReplace),
           Ok("\uc6b0\uc640\ufffd\uc559".to_string()));
~~~~

## Detailed Usage

There are three main entry points to rust-encoding.

**`Encoding`** is a single character encoding.
It contains `encode` and `decode` methods for converting `String` to `Vec<u8>` and vice versa.
For the error handling, they receive **traps** (`EncoderCall` and `DecoderCall` respectively)
which replace any error with some string (e.g. `U+FFFD`) or sequence (e.g. `?`).
You can also use `EncodeStrict` and `DecodeStrict` traps to stop on an error.

There are two ways to get `Encoding`:

* `encoding::all` has static items for every supported encoding.
  You should use them when the encoding would not change or only handful of them are required.
  Combined with link-time optimization, any unused encoding would be discarded from the binary.
* `encoding::label` has functions to dynamically get an encoding from given string ("label").
  They will return a static reference to the encoding, which type is also known as `EncodingRef`.
  It is useful when a list of required encodings is not available in advance,
  but it will result in the larger binary and missed optimization opportunities.

**`Encoder`** is an experimental incremental encoder.
At each step of `raw_feed`, it receives a slice of string
and emits any encoded bytes to a generic `ByteWriter` (normally `Vec<u8>`).
It will stop at the first error if any, and would return a `CodecError` struct in that case.
The caller is responsible for calling `raw_finish` at the end of encoding process.

**`Decoder`** is an experimental incremental decoder.
At each step of `raw_feed`, it receives a slice of byte sequence
and emits any decoded characters to a generic `StringWriter` (normally `String`).
Otherwise it is identical to `Encoder`s.

One should prefer `Encoding::{encode,decode}` as a primary interface.
`Encoder` and `Decoder` is experimental and can change substantially.
See the additional documents on `encoding::types` module for more information on them.

## Supported Encodings

Rust-encoding covers all encodings specified by WHATWG Encoding Standard and some more:

* 7-bit strict ASCII (`ascii`)
* UTF-8 (`utf-8`)
* UTF-16 in little endian (`utf-16` or `utf-16le`) and big endian (`utf-16be`)
* All single byte encoding in WHATWG Encoding Standard:
    * IBM code page 866
    * ISO 8859-{2,3,4,5,6,7,8,10,13,14,15,16}
    * KOI8-R, KOI8-U
    * MacRoman (`macintosh`), Macintosh Cyrillic encoding (`x-mac-cyrillic`)
    * Windows code pages 874, 1250, 1251, 1252 (instead of ISO 8859-1), 1253,
      1254 (instead of ISO 8859-9), 1255, 1256, 1257, 1258
* All multi byte encodings in WHATWG Encoding Standard:
    * Windows code page 949 (`euc-kr`, since the strict EUC-KR is hardly used)
    * EUC-JP and Windows code page 932 (`shift_jis`,
      since it's the most widespread extension to Shift_JIS)
    * ISO-2022-JP with asymmetric JIS X 0212 support
    * GB 18030
    * HZ
    * Big5-2003 with HKSCS-2008 extensions
* ISO 8859-1 (distinct from Windows code page 1252)

Parenthesized names refer to the encoding's primary name assigned by WHATWG Encoding Standard.

Many legacy character encodings lack the proper specification,
and even those that have a specification are highly dependent of the actual implementation.
Consequently one should be careful when picking a desired character encoding.
The only standards reliable in this regard are WHATWG Encoding Standard and
[vendor-provided mappings from the Unicode consortium](http://www.unicode.org/Public/MAPPINGS/).
Whenever in doubt, look at the source code and specifications for detailed explanations.

*/

#![crate_name = "encoding"]
#![crate_type = "lib"]
#![comment = "Character encoding support for Rust"]
#![license = "MIT"]

#![feature(globs, macro_rules)]
#![feature(import_shadowing)] // XXX Rust issue #16597
#![feature(slicing_syntax)]

#![allow(experimental)]

#[cfg(test)] extern crate test;

pub use self::types::{CodecError, ByteWriter, StringWriter,
                      Encoder, Decoder, EncodingRef, Encoding,
                      EncoderTrapFunc, DecoderTrapFunc, DecoderTrap,
                      DecodeStrict, DecodeReplace, DecodeIgnore,
                      EncoderTrap, EncodeStrict, EncodeReplace,
                      EncodeIgnore, EncodeNcrEscape, decode,
                      EncoderCall, DecoderCall}; // reexport

mod util;
#[cfg(test)] mod testutils;

pub mod types;

/// Indices used for character encoding implementation. Semi-internal.
#[unstable]
pub mod index {
    pub mod ibm866;
    pub mod iso_8859_2;
    pub mod iso_8859_3;
    pub mod iso_8859_4;
    pub mod iso_8859_5;
    pub mod iso_8859_6;
    pub mod iso_8859_7;
    pub mod iso_8859_8;
    pub mod iso_8859_10;
    pub mod iso_8859_13;
    pub mod iso_8859_14;
    pub mod iso_8859_15;
    pub mod iso_8859_16;
    pub mod koi8_r;
    pub mod koi8_u;
    pub mod macintosh;
    pub mod windows_874;
    pub mod windows_1250;
    pub mod windows_1251;
    pub mod windows_1252;
    pub mod windows_1253;
    pub mod windows_1254;
    pub mod windows_1255;
    pub mod windows_1256;
    pub mod windows_1257;
    pub mod windows_1258;
    pub mod x_mac_cyrillic;
    pub mod big5;
    pub mod euc_kr;
    pub mod gb18030;
    pub mod gb18030_ranges;
    pub mod jis0208;
    pub mod jis0212;
}

/// Codec implementations.
#[unstable]
pub mod codec {
    pub mod error;
    pub mod ascii;
    pub mod singlebyte;
    pub mod utf_8;
    pub mod utf_16;
    pub mod korean;
    pub mod japanese;
    pub mod simpchinese;
    pub mod tradchinese;
    pub mod whatwg;
}

pub mod all;
pub mod label;

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

    #[test]
    fn test_decode() {
        fn test_one(input: &[u8], expected_result: &str, expected_encoding: &str) {
            let (result, used_encoding) = decode(
                input, DecodeStrict, all::ISO_8859_1 as EncodingRef);
            let result = result.unwrap();
            assert_eq!(used_encoding.name(), expected_encoding);
            assert_eq!(result[], expected_result);
        }

        test_one([0xEF, 0xBB, 0xBF, 0xC3, 0xA9], "é", "utf-8");
        test_one([0xC3, 0xA9], "Ã©", "iso-8859-1");

        test_one([0xFE, 0xFF, 0x00, 0xE9], "é", "utf-16be");
        test_one([0x00, 0xE9], "\x00é", "iso-8859-1");

        test_one([0xFF, 0xFE, 0xE9, 0x00], "é", "utf-16le");
        test_one([0xE9, 0x00], "é\x00", "iso-8859-1");
    }
}

<commit_msg>temporaily cope with the nightly. closes #47.<commit_after>// This is a part of rust-encoding.
// Copyright (c) 2013-2014, Kang Seonghoon.
// See README.md and LICENSE.txt for details.

/*!

# Rust-encoding

Character encoding support for Rust.
It is based on [WHATWG Encoding Standard](http://encoding.spec.whatwg.org/),
and also provides an advanced interface for error detection and recovery.

## Simple Usage

To encode a string:

~~~~ {.rust}
use encoding::{Encoding, EncodeStrict};
use encoding::all::ISO_8859_1;

assert_eq!(ISO_8859_1.encode("caf\xe9", EncodeStrict),
           Ok(vec!(99,97,102,233)));
~~~~

To encode a string with unrepresentable characters:

~~~~ {.rust}
use encoding::{Encoding, EncodeStrict, EncodeReplace, EncodeIgnore, EncodeNcrEscape};
use encoding::all::ISO_8859_2;

assert!(ISO_8859_2.encode("Acme\xa9", EncodeStrict).is_err());
assert_eq!(ISO_8859_2.encode("Acme\xa9", EncodeReplace),
           Ok(vec!(65,99,109,101,63)));
assert_eq!(ISO_8859_2.encode("Acme\xa9", EncodeIgnore),
           Ok(vec!(65,99,109,101)));
assert_eq!(ISO_8859_2.encode("Acme\xa9", EncodeNcrEscape),
           Ok(vec!(65,99,109,101,38,35,49,54,57,59)));
~~~~

To decode a byte sequence:

~~~~ {.rust}
use encoding::{Encoding, DecodeStrict};
use encoding::all::ISO_8859_1;

assert_eq!(ISO_8859_1.decode([99,97,102,233], DecodeStrict),
           Ok("caf\xe9".to_string()));
~~~~

To decode a byte sequence with invalid sequences:

~~~~ {.rust}
use encoding::{Encoding, DecodeStrict, DecodeReplace, DecodeIgnore};
use encoding::all::ISO_8859_6;

assert!(ISO_8859_6.decode([65,99,109,101,169], DecodeStrict).is_err());
assert_eq!(ISO_8859_6.decode([65,99,109,101,169], DecodeReplace),
           Ok("Acme\ufffd".to_string()));
assert_eq!(ISO_8859_6.decode([65,99,109,101,169], DecodeIgnore),
           Ok("Acme".to_string()));
~~~~

A practical example of custom encoder traps:

~~~~ {.rust}
use encoding::{Encoding, Encoder, ByteWriter, EncoderTrap, EncoderCall, DecodeStrict};
use encoding::all::ASCII;

// hexadecimal numeric character reference replacement
fn hex_ncr_escape(_encoder: &mut Encoder, input: &str, output: &mut ByteWriter) -> bool {
    let escapes: Vec<String> =
        input.chars().map(|ch| format!("&#x{:x};", ch as int)).collect();
    let escapes = escapes.concat();
    output.write_bytes(escapes.as_bytes());
    true
}
static HexNcrEscape: EncoderTrap = EncoderCall(hex_ncr_escape);

let orig = "Hello, 世界!".to_string();
let encoded = ASCII.encode(orig[], HexNcrEscape).unwrap();
assert_eq!(ASCII.decode(encoded[], DecodeStrict),
           Ok("Hello, 世界!".to_string()));
~~~~

Getting the encoding from the string label, as specified in WHATWG Encoding standard:

~~~~ {.rust}
use encoding::{Encoding, DecodeReplace};
use encoding::label::encoding_from_whatwg_label;
use encoding::all::WINDOWS_949;

let euckr = encoding_from_whatwg_label("euc-kr").unwrap();
assert_eq!(euckr.name(), "windows-949");
assert_eq!(euckr.whatwg_name(), Some("euc-kr")); // for the sake of compatibility
let broken = &[0xbf, 0xec, 0xbf, 0xcd, 0xff, 0xbe, 0xd3];
assert_eq!(euckr.decode(broken, DecodeReplace),
           Ok("\uc6b0\uc640\ufffd\uc559".to_string()));

// corresponding rust-encoding native API:
assert_eq!(WINDOWS_949.decode(broken, DecodeReplace),
           Ok("\uc6b0\uc640\ufffd\uc559".to_string()));
~~~~

## Detailed Usage

There are three main entry points to rust-encoding.

**`Encoding`** is a single character encoding.
It contains `encode` and `decode` methods for converting `String` to `Vec<u8>` and vice versa.
For the error handling, they receive **traps** (`EncoderCall` and `DecoderCall` respectively)
which replace any error with some string (e.g. `U+FFFD`) or sequence (e.g. `?`).
You can also use `EncodeStrict` and `DecodeStrict` traps to stop on an error.

There are two ways to get `Encoding`:

* `encoding::all` has static items for every supported encoding.
  You should use them when the encoding would not change or only handful of them are required.
  Combined with link-time optimization, any unused encoding would be discarded from the binary.
* `encoding::label` has functions to dynamically get an encoding from given string ("label").
  They will return a static reference to the encoding, which type is also known as `EncodingRef`.
  It is useful when a list of required encodings is not available in advance,
  but it will result in the larger binary and missed optimization opportunities.

**`Encoder`** is an experimental incremental encoder.
At each step of `raw_feed`, it receives a slice of string
and emits any encoded bytes to a generic `ByteWriter` (normally `Vec<u8>`).
It will stop at the first error if any, and would return a `CodecError` struct in that case.
The caller is responsible for calling `raw_finish` at the end of encoding process.

**`Decoder`** is an experimental incremental decoder.
At each step of `raw_feed`, it receives a slice of byte sequence
and emits any decoded characters to a generic `StringWriter` (normally `String`).
Otherwise it is identical to `Encoder`s.

One should prefer `Encoding::{encode,decode}` as a primary interface.
`Encoder` and `Decoder` is experimental and can change substantially.
See the additional documents on `encoding::types` module for more information on them.

## Supported Encodings

Rust-encoding covers all encodings specified by WHATWG Encoding Standard and some more:

* 7-bit strict ASCII (`ascii`)
* UTF-8 (`utf-8`)
* UTF-16 in little endian (`utf-16` or `utf-16le`) and big endian (`utf-16be`)
* All single byte encoding in WHATWG Encoding Standard:
    * IBM code page 866
    * ISO 8859-{2,3,4,5,6,7,8,10,13,14,15,16}
    * KOI8-R, KOI8-U
    * MacRoman (`macintosh`), Macintosh Cyrillic encoding (`x-mac-cyrillic`)
    * Windows code pages 874, 1250, 1251, 1252 (instead of ISO 8859-1), 1253,
      1254 (instead of ISO 8859-9), 1255, 1256, 1257, 1258
* All multi byte encodings in WHATWG Encoding Standard:
    * Windows code page 949 (`euc-kr`, since the strict EUC-KR is hardly used)
    * EUC-JP and Windows code page 932 (`shift_jis`,
      since it's the most widespread extension to Shift_JIS)
    * ISO-2022-JP with asymmetric JIS X 0212 support
    * GB 18030
    * HZ
    * Big5-2003 with HKSCS-2008 extensions
* ISO 8859-1 (distinct from Windows code page 1252)

Parenthesized names refer to the encoding's primary name assigned by WHATWG Encoding Standard.

Many legacy character encodings lack the proper specification,
and even those that have a specification are highly dependent of the actual implementation.
Consequently one should be careful when picking a desired character encoding.
The only standards reliable in this regard are WHATWG Encoding Standard and
[vendor-provided mappings from the Unicode consortium](http://www.unicode.org/Public/MAPPINGS/).
Whenever in doubt, look at the source code and specifications for detailed explanations.

*/

#![crate_name = "encoding"]
#![crate_type = "lib"]
#![comment = "Character encoding support for Rust"]
#![license = "MIT"]

#![feature(globs, macro_rules)]

#![allow(experimental)]

// XXX temporarily cope with the nightly, see #47
#![allow(unknown_features)]
#![feature(slicing_syntax)]

#[cfg(test)] extern crate test;

pub use self::types::{CodecError, ByteWriter, StringWriter,
                      Encoder, Decoder, EncodingRef, Encoding,
                      EncoderTrapFunc, DecoderTrapFunc, DecoderTrap,
                      DecodeStrict, DecodeReplace, DecodeIgnore,
                      EncoderTrap, EncodeStrict, EncodeReplace,
                      EncodeIgnore, EncodeNcrEscape, decode,
                      EncoderCall, DecoderCall}; // reexport

mod util;
#[cfg(test)] mod testutils;

pub mod types;

/// Indices used for character encoding implementation. Semi-internal.
#[unstable]
pub mod index {
    pub mod ibm866;
    pub mod iso_8859_2;
    pub mod iso_8859_3;
    pub mod iso_8859_4;
    pub mod iso_8859_5;
    pub mod iso_8859_6;
    pub mod iso_8859_7;
    pub mod iso_8859_8;
    pub mod iso_8859_10;
    pub mod iso_8859_13;
    pub mod iso_8859_14;
    pub mod iso_8859_15;
    pub mod iso_8859_16;
    pub mod koi8_r;
    pub mod koi8_u;
    pub mod macintosh;
    pub mod windows_874;
    pub mod windows_1250;
    pub mod windows_1251;
    pub mod windows_1252;
    pub mod windows_1253;
    pub mod windows_1254;
    pub mod windows_1255;
    pub mod windows_1256;
    pub mod windows_1257;
    pub mod windows_1258;
    pub mod x_mac_cyrillic;
    pub mod big5;
    pub mod euc_kr;
    pub mod gb18030;
    pub mod gb18030_ranges;
    pub mod jis0208;
    pub mod jis0212;
}

/// Codec implementations.
#[unstable]
pub mod codec {
    pub mod error;
    pub mod ascii;
    pub mod singlebyte;
    pub mod utf_8;
    pub mod utf_16;
    pub mod korean;
    pub mod japanese;
    pub mod simpchinese;
    pub mod tradchinese;
    pub mod whatwg;
}

pub mod all;
pub mod label;

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

    #[test]
    fn test_decode() {
        fn test_one(input: &[u8], expected_result: &str, expected_encoding: &str) {
            let (result, used_encoding) = decode(
                input, DecodeStrict, all::ISO_8859_1 as EncodingRef);
            let result = result.unwrap();
            assert_eq!(used_encoding.name(), expected_encoding);
            assert_eq!(result[], expected_result);
        }

        test_one([0xEF, 0xBB, 0xBF, 0xC3, 0xA9], "é", "utf-8");
        test_one([0xC3, 0xA9], "Ã©", "iso-8859-1");

        test_one([0xFE, 0xFF, 0x00, 0xE9], "é", "utf-16be");
        test_one([0x00, 0xE9], "\x00é", "iso-8859-1");

        test_one([0xFF, 0xFE, 0xE9, 0x00], "é", "utf-16le");
        test_one([0xE9, 0x00], "é\x00", "iso-8859-1");
    }
}

<|endoftext|>