<commit_before>// Copyright 2018 The xi-editor Authors.
//
// 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.

//! Interactions with the file system.

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use tabs::BufferId;

#[cfg(feature = "notify")]
use tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use watcher::FileWatcher;

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            let _ = File::create(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8)
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding).map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mod_time =
        f.metadata().map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?.modified().ok();
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo { encoding, mod_time, path: path.as_ref().to_owned(), has_changed: false };
    Ok((rope, info))
}

fn try_save(path: &Path, text: &Rope, encoding: CharacterEncoding) -> io::Result<()> {
    use std::fs;

    let tmp_extension = path
        .extension()
        .and_then(|extension| extension.to_str())
        .map_or_else(|| "swp".to_string(), |extension| format!("{}.swp", extension));
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P>(path: P) -> Option<SystemTime>
where
    P: AsRef<Path>,
{
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<commit_msg>Prefer string concat to format<commit_after>// Copyright 2018 The xi-editor Authors.
//
// 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.

//! Interactions with the file system.

use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str;
use std::time::SystemTime;

use xi_rope::Rope;
use xi_rpc::RemoteError;

use tabs::BufferId;

#[cfg(feature = "notify")]
use tabs::OPEN_FILE_EVENT_TOKEN;
#[cfg(feature = "notify")]
use watcher::FileWatcher;

const UTF8_BOM: &str = "\u{feff}";

/// Tracks all state related to open files.
pub struct FileManager {
    open_files: HashMap<PathBuf, BufferId>,
    file_info: HashMap<BufferId, FileInfo>,
    /// A monitor of filesystem events, for things like reloading changed files.
    #[cfg(feature = "notify")]
    watcher: FileWatcher,
}

#[derive(Debug)]
pub struct FileInfo {
    pub encoding: CharacterEncoding,
    pub path: PathBuf,
    pub mod_time: Option<SystemTime>,
    pub has_changed: bool,
}

pub enum FileError {
    Io(io::Error, PathBuf),
    UnknownEncoding(PathBuf),
    HasChanged(PathBuf),
}

#[derive(Debug, Clone, Copy)]
pub enum CharacterEncoding {
    Utf8,
    Utf8WithBom,
}

impl FileManager {
    #[cfg(feature = "notify")]
    pub fn new(watcher: FileWatcher) -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new(), watcher }
    }

    #[cfg(not(feature = "notify"))]
    pub fn new() -> Self {
        FileManager { open_files: HashMap::new(), file_info: HashMap::new() }
    }

    #[cfg(feature = "notify")]
    pub fn watcher(&mut self) -> &mut FileWatcher {
        &mut self.watcher
    }

    pub fn get_info(&self, id: BufferId) -> Option<&FileInfo> {
        self.file_info.get(&id)
    }

    pub fn get_editor(&self, path: &Path) -> Option<BufferId> {
        self.open_files.get(path).cloned()
    }

    /// Returns `true` if this file is open and has changed on disk.
    /// This state is stashed.
    pub fn check_file(&mut self, path: &Path, id: BufferId) -> bool {
        if let Some(info) = self.file_info.get_mut(&id) {
            let mod_t = get_mod_time(path);
            if mod_t != info.mod_time {
                info.has_changed = true
            }
            return info.has_changed;
        }
        false
    }

    pub fn open(&mut self, path: &Path, id: BufferId) -> Result<Rope, FileError> {
        if !path.exists() {
            let _ = File::create(path).map_err(|e| FileError::Io(e, path.to_owned()))?;
        }

        let (rope, info) = try_load_file(path)?;

        self.open_files.insert(path.to_owned(), id);
        if self.file_info.insert(id, info).is_none() {
            #[cfg(feature = "notify")]
            self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        }
        Ok(rope)
    }

    pub fn close(&mut self, id: BufferId) {
        if let Some(info) = self.file_info.remove(&id) {
            self.open_files.remove(&info.path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&info.path, OPEN_FILE_EVENT_TOKEN);
        }
    }

    pub fn save(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let is_existing = self.file_info.contains_key(&id);
        if is_existing {
            self.save_existing(path, text, id)
        } else {
            self.save_new(path, text, id)
        }
    }

    fn save_new(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        try_save(path, text, CharacterEncoding::Utf8)
            .map_err(|e| FileError::Io(e, path.to_owned()))?;
        let info = FileInfo {
            encoding: CharacterEncoding::Utf8,
            path: path.to_owned(),
            mod_time: get_mod_time(path),
            has_changed: false,
        };
        self.open_files.insert(path.to_owned(), id);
        self.file_info.insert(id, info);
        #[cfg(feature = "notify")]
        self.watcher.watch(path, false, OPEN_FILE_EVENT_TOKEN);
        Ok(())
    }

    fn save_existing(&mut self, path: &Path, text: &Rope, id: BufferId) -> Result<(), FileError> {
        let prev_path = self.file_info[&id].path.clone();
        if prev_path != path {
            self.save_new(path, text, id)?;
            self.open_files.remove(&prev_path);
            #[cfg(feature = "notify")]
            self.watcher.unwatch(&prev_path, OPEN_FILE_EVENT_TOKEN);
        } else if self.file_info[&id].has_changed {
            return Err(FileError::HasChanged(path.to_owned()));
        } else {
            let encoding = self.file_info[&id].encoding;
            try_save(path, text, encoding).map_err(|e| FileError::Io(e, path.to_owned()))?;
            self.file_info.get_mut(&id).unwrap().mod_time = get_mod_time(path);
        }
        Ok(())
    }
}

fn try_load_file<P>(path: P) -> Result<(Rope, FileInfo), FileError>
where
    P: AsRef<Path>,
{
    // TODO: support for non-utf8
    // it's arguable that the rope crate should have file loading functionality
    let mut f =
        File::open(path.as_ref()).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;
    let mod_time =
        f.metadata().map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?.modified().ok();
    let mut bytes = Vec::new();
    f.read_to_end(&mut bytes).map_err(|e| FileError::Io(e, path.as_ref().to_owned()))?;

    let encoding = CharacterEncoding::guess(&bytes);
    let rope = try_decode(bytes, encoding, path.as_ref())?;
    let info = FileInfo { encoding, mod_time, path: path.as_ref().to_owned(), has_changed: false };
    Ok((rope, info))
}

fn try_save(path: &Path, text: &Rope, encoding: CharacterEncoding) -> io::Result<()> {
    use std::fs;

    let tmp_extension = path
        .extension()
        .and_then(|extension| extension.to_str())
        .map_or_else(|| "swp".to_string(), |extension| extension.to_string() + ".swp");
    let tmp_path = &path.with_extension(tmp_extension);

    let mut f = File::create(tmp_path)?;
    match encoding {
        CharacterEncoding::Utf8WithBom => f.write_all(UTF8_BOM.as_bytes())?,
        CharacterEncoding::Utf8 => (),
    }

    for chunk in text.iter_chunks(..text.len()) {
        f.write_all(chunk.as_bytes())?;
    }

    fs::rename(tmp_path, path)?;

    Ok(())
}

fn try_decode(bytes: Vec<u8>, encoding: CharacterEncoding, path: &Path) -> Result<Rope, FileError> {
    match encoding {
        CharacterEncoding::Utf8 => Ok(Rope::from(
            str::from_utf8(&bytes).map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?,
        )),
        CharacterEncoding::Utf8WithBom => {
            let s = String::from_utf8(bytes)
                .map_err(|_e| FileError::UnknownEncoding(path.to_owned()))?;
            Ok(Rope::from(&s[UTF8_BOM.len()..]))
        }
    }
}

impl CharacterEncoding {
    fn guess(s: &[u8]) -> Self {
        if s.starts_with(UTF8_BOM.as_bytes()) {
            CharacterEncoding::Utf8WithBom
        } else {
            CharacterEncoding::Utf8
        }
    }
}

/// Returns the modification timestamp for the file at a given path,
/// if present.
fn get_mod_time<P>(path: P) -> Option<SystemTime>
where
    P: AsRef<Path>,
{
    File::open(path).and_then(|f| f.metadata()).and_then(|meta| meta.modified()).ok()
}

impl From<FileError> for RemoteError {
    fn from(src: FileError) -> RemoteError {
        //TODO: when we migrate to using the failure crate for error handling,
        // this should return a better message
        let code = src.error_code();
        let message = src.to_string();
        RemoteError::custom(code, message, None)
    }
}

impl FileError {
    fn error_code(&self) -> i64 {
        match self {
            FileError::Io(_, _) => 5,
            FileError::UnknownEncoding(_) => 6,
            FileError::HasChanged(_) => 7,
        }
    }
}

impl fmt::Display for FileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            FileError::Io(ref e, ref p) => write!(f, "{}. File path: {:?}", e, p),
            FileError::UnknownEncoding(ref p) => write!(f, "Error decoding file: {:?}", p),
            FileError::HasChanged(ref p) => write!(
                f,
                "File has changed on disk. \
                 Please save elsewhere and reload the file. File path: {:?}",
                p
            ),
        }
    }
}
<|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/. */

//! DOM bindings for `CharacterData`.

use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
use dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId};
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::root::{DomRoot, LayoutDom};
use dom::bindings::str::DOMString;
use dom::comment::Comment;
use dom::document::Document;
use dom::element::Element;
use dom::node::{ChildrenMutation, Node, NodeDamage};
use dom::processinginstruction::ProcessingInstruction;
use dom::text::Text;
use dom::virtualmethods::vtable_for;
use dom_struct::dom_struct;
use servo_config::opts;
use std::cell::Ref;

// https://dom.spec.whatwg.org/#characterdata
#[dom_struct]
pub struct CharacterData {
    node: Node,
    data: DomRefCell<DOMString>,
}

impl CharacterData {
    pub fn new_inherited(data: DOMString, document: &Document) -> CharacterData {
        CharacterData {
            node: Node::new_inherited(document),
            data: DomRefCell::new(data),
        }
    }

    pub fn clone_with_data(&self, data: DOMString, document: &Document) -> DomRoot<Node> {
        match self.upcast::<Node>().type_id() {
            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => {
                DomRoot::upcast(Comment::new(data, &document))
            }
            NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
                let pi = self.downcast::<ProcessingInstruction>().unwrap();
                DomRoot::upcast(ProcessingInstruction::new(pi.Target(), data, &document))
            },
            NodeTypeId::CharacterData(CharacterDataTypeId::Text) => {
                DomRoot::upcast(Text::new(data, &document))
            },
            _ => unreachable!(),
        }
    }

    #[inline]
    pub fn data(&self) -> Ref<DOMString> {
        self.data.borrow()
    }

    #[inline]
    pub fn append_data(&self, data: &str) {
        self.data.borrow_mut().push_str(data);
        self.content_changed();
    }

    fn content_changed(&self) {
        let node = self.upcast::<Node>();
        node.dirty(NodeDamage::OtherNodeDamage);
    }
}

impl CharacterDataMethods for CharacterData {
    // https://dom.spec.whatwg.org/#dom-characterdata-data
    fn Data(&self) -> DOMString {
        self.data.borrow().clone()
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-data
    fn SetData(&self, data: DOMString) {
        let old_length = self.Length();
        let new_length = data.encode_utf16().count() as u32;
        *self.data.borrow_mut() = data;
        self.content_changed();
        let node = self.upcast::<Node>();
        node.ranges().replace_code_units(node, 0, old_length, new_length);

        // If this is a Text node, we might need to re-parse (say, if our parent
        // is a <style> element.) We don't need to if this is a Comment or
        // ProcessingInstruction.
        if self.is::<Text>() {
            if let Some(parent_node) = node.GetParentNode() {
                let mutation = ChildrenMutation::ChangeText;
                vtable_for(&parent_node).children_changed(&mutation);
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-length
    fn Length(&self) -> u32 {
        self.data.borrow().encode_utf16().count() as u32
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-substringdata
    fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString> {
        let data = self.data.borrow();
        // Step 1.
        let mut substring = String::new();
        let remaining;
        match split_at_utf16_code_unit_offset(&data, offset) {
            Ok((_, astral, s)) => {
                // As if we had split the UTF-16 surrogate pair in half
                // and then transcoded that to UTF-8 lossily,
                // since our DOMString is currently strict UTF-8.
                if astral.is_some() {
                    substring = substring + "\u{FFFD}";
                }
                remaining = s;
            }
            // Step 2.
            Err(()) => return Err(Error::IndexSize),
        }
        match split_at_utf16_code_unit_offset(remaining, count) {
            // Steps 3.
            Err(()) => substring = substring + remaining,
            // Steps 4.
            Ok((s, astral, _)) => {
                substring = substring + s;
                // As if we had split the UTF-16 surrogate pair in half
                // and then transcoded that to UTF-8 lossily,
                // since our DOMString is currently strict UTF-8.
                if astral.is_some() {
                    substring = substring + "\u{FFFD}";
                }
            }
        };
        Ok(DOMString::from(substring))
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-appenddatadata
    fn AppendData(&self, data: DOMString) {
        // FIXME(ajeffrey): Efficient append on DOMStrings?
        self.append_data(&*data);
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-insertdataoffset-data
    fn InsertData(&self, offset: u32, arg: DOMString) -> ErrorResult {
        self.ReplaceData(offset, 0, arg)
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-deletedataoffset-count
    fn DeleteData(&self, offset: u32, count: u32) -> ErrorResult {
        self.ReplaceData(offset, count, DOMString::new())
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-replacedata
    fn ReplaceData(&self, offset: u32, count: u32, arg: DOMString) -> ErrorResult {
        let mut new_data;
        {
            let data = self.data.borrow();
            let prefix;
            let replacement_before;
            let remaining;
            match split_at_utf16_code_unit_offset(&data, offset) {
                Ok((p, astral, r)) => {
                    prefix = p;
                    // As if we had split the UTF-16 surrogate pair in half
                    // and then transcoded that to UTF-8 lossily,
                    // since our DOMString is currently strict UTF-8.
                    replacement_before = if astral.is_some() { "\u{FFFD}" } else { "" };
                    remaining = r;
                }
                // Step 2.
                Err(()) => return Err(Error::IndexSize),
            };
            let replacement_after;
            let suffix;
            match split_at_utf16_code_unit_offset(remaining, count) {
                // Steps 3.
                Err(()) => {
                    replacement_after = "";
                    suffix = "";
                }
                Ok((_, astral, s)) => {
                    // As if we had split the UTF-16 surrogate pair in half
                    // and then transcoded that to UTF-8 lossily,
                    // since our DOMString is currently strict UTF-8.
                    replacement_after = if astral.is_some() { "\u{FFFD}" } else { "" };
                    suffix = s;
                }
            };
            // Step 4: Mutation observers.
            // Step 5 to 7.
            new_data = String::with_capacity(
                prefix.len() +
                replacement_before.len() +
                arg.len() +
                replacement_after.len() +
                suffix.len());
            new_data.push_str(prefix);
            new_data.push_str(replacement_before);
            new_data.push_str(&arg);
            new_data.push_str(replacement_after);
            new_data.push_str(suffix);
        }
        *self.data.borrow_mut() = DOMString::from(new_data);
        self.content_changed();
        // Steps 8-11.
        let node = self.upcast::<Node>();
        node.ranges().replace_code_units(
            node, offset, count, arg.encode_utf16().count() as u32);
        Ok(())
    }

    // https://dom.spec.whatwg.org/#dom-childnode-before
    fn Before(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
        self.upcast::<Node>().before(nodes)
    }

    // https://dom.spec.whatwg.org/#dom-childnode-after
    fn After(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
        self.upcast::<Node>().after(nodes)
    }

    // https://dom.spec.whatwg.org/#dom-childnode-replacewith
    fn ReplaceWith(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
        self.upcast::<Node>().replace_with(nodes)
    }

    // https://dom.spec.whatwg.org/#dom-childnode-remove
    fn Remove(&self) {
        let node = self.upcast::<Node>();
        node.remove_self();
    }

    // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling
    fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
        self.upcast::<Node>().preceding_siblings().filter_map(DomRoot::downcast).next()
    }

    // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling
    fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
        self.upcast::<Node>().following_siblings().filter_map(DomRoot::downcast).next()
    }
}

#[allow(unsafe_code)]
pub trait LayoutCharacterDataHelpers {
    unsafe fn data_for_layout(&self) -> &str;
}

#[allow(unsafe_code)]
impl LayoutCharacterDataHelpers for LayoutDom<CharacterData> {
    #[inline]
    unsafe fn data_for_layout(&self) -> &str {
        &(*self.unsafe_get()).data.borrow_for_layout()
    }
}

/// Split the given string at the given position measured in UTF-16 code units from the start.
///
/// * `Err(())` indicates that `offset` if after the end of the string
/// * `Ok((before, None, after))` indicates that `offset` is between Unicode code points.
///   The two string slices are such that:
///   `before == s.to_utf16()[..offset].to_utf8()` and
///   `after == s.to_utf16()[offset..].to_utf8()`
/// * `Ok((before, Some(ch), after))` indicates that `offset` is "in the middle"
///   of a single Unicode code point that would be represented in UTF-16 by a surrogate pair
///   of two 16-bit code units.
///   `ch` is that code point.
///   The two string slices are such that:
///   `before == s.to_utf16()[..offset - 1].to_utf8()` and
///   `after == s.to_utf16()[offset + 1..].to_utf8()`
///
/// # Panics
///
/// Note that the third variant is only ever returned when the `-Z replace-surrogates`
/// command-line option is specified.
/// When it *would* be returned but the option is *not* specified, this function panics.
fn split_at_utf16_code_unit_offset(s: &str, offset: u32) -> Result<(&str, Option<char>, &str), ()> {
    let mut code_units = 0;
    for (i, c) in s.char_indices() {
        if code_units == offset {
            let (a, b) = s.split_at(i);
            return Ok((a, None, b));
        }
        code_units += 1;
        if c > '\u{FFFF}' {
            if code_units == offset {
                if opts::get().replace_surrogates {
                    debug_assert!(c.len_utf8() == 4);
                    return Ok((&s[..i], Some(c), &s[i + c.len_utf8()..]))
                }
                panic!("\n\n\
                    Would split a surrogate pair in CharacterData API.\n\
                    If you see this in real content, please comment with the URL\n\
                    on https://github.com/servo/servo/issues/6873\n\
                \n");
            }
            code_units += 1;
        }
    }
    if code_units == offset {
        Ok((s, None, ""))
    } else {
        Err(())
    }
}
<commit_msg>Call children_changed on the parent node consistently<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/. */

//! DOM bindings for `CharacterData`.

use dom::bindings::cell::DomRefCell;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
use dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId};
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::root::{DomRoot, LayoutDom};
use dom::bindings::str::DOMString;
use dom::comment::Comment;
use dom::document::Document;
use dom::element::Element;
use dom::node::{ChildrenMutation, Node, NodeDamage};
use dom::processinginstruction::ProcessingInstruction;
use dom::text::Text;
use dom::virtualmethods::vtable_for;
use dom_struct::dom_struct;
use servo_config::opts;
use std::cell::Ref;

// https://dom.spec.whatwg.org/#characterdata
#[dom_struct]
pub struct CharacterData {
    node: Node,
    data: DomRefCell<DOMString>,
}

impl CharacterData {
    pub fn new_inherited(data: DOMString, document: &Document) -> CharacterData {
        CharacterData {
            node: Node::new_inherited(document),
            data: DomRefCell::new(data),
        }
    }

    pub fn clone_with_data(&self, data: DOMString, document: &Document) -> DomRoot<Node> {
        match self.upcast::<Node>().type_id() {
            NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => {
                DomRoot::upcast(Comment::new(data, &document))
            }
            NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
                let pi = self.downcast::<ProcessingInstruction>().unwrap();
                DomRoot::upcast(ProcessingInstruction::new(pi.Target(), data, &document))
            },
            NodeTypeId::CharacterData(CharacterDataTypeId::Text) => {
                DomRoot::upcast(Text::new(data, &document))
            },
            _ => unreachable!(),
        }
    }

    #[inline]
    pub fn data(&self) -> Ref<DOMString> {
        self.data.borrow()
    }

    #[inline]
    pub fn append_data(&self, data: &str) {
        self.data.borrow_mut().push_str(data);
        self.content_changed();
    }

    fn content_changed(&self) {
        let node = self.upcast::<Node>();
        node.dirty(NodeDamage::OtherNodeDamage);

        // If this is a Text node, we might need to re-parse (say, if our parent
        // is a <style> element.) We don't need to if this is a Comment or
        // ProcessingInstruction.
        if self.is::<Text>() {
            if let Some(parent_node) = node.GetParentNode() {
                let mutation = ChildrenMutation::ChangeText;
                vtable_for(&parent_node).children_changed(&mutation);
            }
        }
    }
}

impl CharacterDataMethods for CharacterData {
    // https://dom.spec.whatwg.org/#dom-characterdata-data
    fn Data(&self) -> DOMString {
        self.data.borrow().clone()
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-data
    fn SetData(&self, data: DOMString) {
        let old_length = self.Length();
        let new_length = data.encode_utf16().count() as u32;
        *self.data.borrow_mut() = data;
        self.content_changed();
        let node = self.upcast::<Node>();
        node.ranges().replace_code_units(node, 0, old_length, new_length);
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-length
    fn Length(&self) -> u32 {
        self.data.borrow().encode_utf16().count() as u32
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-substringdata
    fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString> {
        let data = self.data.borrow();
        // Step 1.
        let mut substring = String::new();
        let remaining;
        match split_at_utf16_code_unit_offset(&data, offset) {
            Ok((_, astral, s)) => {
                // As if we had split the UTF-16 surrogate pair in half
                // and then transcoded that to UTF-8 lossily,
                // since our DOMString is currently strict UTF-8.
                if astral.is_some() {
                    substring = substring + "\u{FFFD}";
                }
                remaining = s;
            }
            // Step 2.
            Err(()) => return Err(Error::IndexSize),
        }
        match split_at_utf16_code_unit_offset(remaining, count) {
            // Steps 3.
            Err(()) => substring = substring + remaining,
            // Steps 4.
            Ok((s, astral, _)) => {
                substring = substring + s;
                // As if we had split the UTF-16 surrogate pair in half
                // and then transcoded that to UTF-8 lossily,
                // since our DOMString is currently strict UTF-8.
                if astral.is_some() {
                    substring = substring + "\u{FFFD}";
                }
            }
        };
        Ok(DOMString::from(substring))
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-appenddatadata
    fn AppendData(&self, data: DOMString) {
        // FIXME(ajeffrey): Efficient append on DOMStrings?
        self.append_data(&*data);
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-insertdataoffset-data
    fn InsertData(&self, offset: u32, arg: DOMString) -> ErrorResult {
        self.ReplaceData(offset, 0, arg)
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-deletedataoffset-count
    fn DeleteData(&self, offset: u32, count: u32) -> ErrorResult {
        self.ReplaceData(offset, count, DOMString::new())
    }

    // https://dom.spec.whatwg.org/#dom-characterdata-replacedata
    fn ReplaceData(&self, offset: u32, count: u32, arg: DOMString) -> ErrorResult {
        let mut new_data;
        {
            let data = self.data.borrow();
            let prefix;
            let replacement_before;
            let remaining;
            match split_at_utf16_code_unit_offset(&data, offset) {
                Ok((p, astral, r)) => {
                    prefix = p;
                    // As if we had split the UTF-16 surrogate pair in half
                    // and then transcoded that to UTF-8 lossily,
                    // since our DOMString is currently strict UTF-8.
                    replacement_before = if astral.is_some() { "\u{FFFD}" } else { "" };
                    remaining = r;
                }
                // Step 2.
                Err(()) => return Err(Error::IndexSize),
            };
            let replacement_after;
            let suffix;
            match split_at_utf16_code_unit_offset(remaining, count) {
                // Steps 3.
                Err(()) => {
                    replacement_after = "";
                    suffix = "";
                }
                Ok((_, astral, s)) => {
                    // As if we had split the UTF-16 surrogate pair in half
                    // and then transcoded that to UTF-8 lossily,
                    // since our DOMString is currently strict UTF-8.
                    replacement_after = if astral.is_some() { "\u{FFFD}" } else { "" };
                    suffix = s;
                }
            };
            // Step 4: Mutation observers.
            // Step 5 to 7.
            new_data = String::with_capacity(
                prefix.len() +
                replacement_before.len() +
                arg.len() +
                replacement_after.len() +
                suffix.len());
            new_data.push_str(prefix);
            new_data.push_str(replacement_before);
            new_data.push_str(&arg);
            new_data.push_str(replacement_after);
            new_data.push_str(suffix);
        }
        *self.data.borrow_mut() = DOMString::from(new_data);
        self.content_changed();
        // Steps 8-11.
        let node = self.upcast::<Node>();
        node.ranges().replace_code_units(
            node, offset, count, arg.encode_utf16().count() as u32);
        Ok(())
    }

    // https://dom.spec.whatwg.org/#dom-childnode-before
    fn Before(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
        self.upcast::<Node>().before(nodes)
    }

    // https://dom.spec.whatwg.org/#dom-childnode-after
    fn After(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
        self.upcast::<Node>().after(nodes)
    }

    // https://dom.spec.whatwg.org/#dom-childnode-replacewith
    fn ReplaceWith(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
        self.upcast::<Node>().replace_with(nodes)
    }

    // https://dom.spec.whatwg.org/#dom-childnode-remove
    fn Remove(&self) {
        let node = self.upcast::<Node>();
        node.remove_self();
    }

    // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling
    fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
        self.upcast::<Node>().preceding_siblings().filter_map(DomRoot::downcast).next()
    }

    // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling
    fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
        self.upcast::<Node>().following_siblings().filter_map(DomRoot::downcast).next()
    }
}

#[allow(unsafe_code)]
pub trait LayoutCharacterDataHelpers {
    unsafe fn data_for_layout(&self) -> &str;
}

#[allow(unsafe_code)]
impl LayoutCharacterDataHelpers for LayoutDom<CharacterData> {
    #[inline]
    unsafe fn data_for_layout(&self) -> &str {
        &(*self.unsafe_get()).data.borrow_for_layout()
    }
}

/// Split the given string at the given position measured in UTF-16 code units from the start.
///
/// * `Err(())` indicates that `offset` if after the end of the string
/// * `Ok((before, None, after))` indicates that `offset` is between Unicode code points.
///   The two string slices are such that:
///   `before == s.to_utf16()[..offset].to_utf8()` and
///   `after == s.to_utf16()[offset..].to_utf8()`
/// * `Ok((before, Some(ch), after))` indicates that `offset` is "in the middle"
///   of a single Unicode code point that would be represented in UTF-16 by a surrogate pair
///   of two 16-bit code units.
///   `ch` is that code point.
///   The two string slices are such that:
///   `before == s.to_utf16()[..offset - 1].to_utf8()` and
///   `after == s.to_utf16()[offset + 1..].to_utf8()`
///
/// # Panics
///
/// Note that the third variant is only ever returned when the `-Z replace-surrogates`
/// command-line option is specified.
/// When it *would* be returned but the option is *not* specified, this function panics.
fn split_at_utf16_code_unit_offset(s: &str, offset: u32) -> Result<(&str, Option<char>, &str), ()> {
    let mut code_units = 0;
    for (i, c) in s.char_indices() {
        if code_units == offset {
            let (a, b) = s.split_at(i);
            return Ok((a, None, b));
        }
        code_units += 1;
        if c > '\u{FFFF}' {
            if code_units == offset {
                if opts::get().replace_surrogates {
                    debug_assert!(c.len_utf8() == 4);
                    return Ok((&s[..i], Some(c), &s[i + c.len_utf8()..]))
                }
                panic!("\n\n\
                    Would split a surrogate pair in CharacterData API.\n\
                    If you see this in real content, please comment with the URL\n\
                    on https://github.com/servo/servo/issues/6873\n\
                \n");
            }
            code_units += 1;
        }
    }
    if code_units == offset {
        Ok((s, None, ""))
    } else {
        Err(())
    }
}
<|endoftext|>


<commit_before>use read_write::*;
use service::*;
use libc::*;
use std::mem::transmute;

pub static BACKEND_API_VERSION: &'static [u8] = b"ProDBG Backend 1\0";

pub trait Backend {
    fn new(service: &Service) -> Self;
    fn update(&mut self, action: i32, reader: &mut Reader, writer: &mut Writer);
}

pub type ServiceFunc = extern "C" fn(service: *const c_uchar) -> *mut c_void;

#[repr(C)]
pub struct CBackendCallbacks {
    pub name: *const c_uchar,
    pub create_instance: Option<fn(service_func: ServiceFunc) -> *mut c_void>,
    pub destroy_instance: Option<fn(*mut c_void)>,
    pub register_menu: Option<fn(ptr: *mut c_void, menu_funcs: *mut c_void) -> *mut c_void>,
    pub update: Option<fn(ptr: *mut c_void,
                          a: c_int,
                          ra: *mut c_void,
                          wa: *mut c_void)>,
}

unsafe impl Sync for CBackendCallbacks {}

pub fn create_backend_instance<T: Backend>(service_func: extern "C" fn(service: *const c_uchar)
                                                                       -> *mut c_void)
                                           -> *mut c_void {
    let service = Service { service_func: service_func };
    let instance = unsafe { transmute(Box::new(T::new(&service))) };
    println!("Lets create instance!");
    instance
}

pub fn destroy_backend_instance<T: Backend>(ptr: *mut c_void) {
    println!("rust: backend: destroy");
    let _: Box<T> = unsafe { transmute(ptr) };
    // implicitly dropped
}

pub fn update_backend_instance<T: Backend>(ptr: *mut c_void,
                                           action: c_int,
                                           reader_api: *mut c_void,
                                           writer_api: *mut c_void) {
    let backend: &mut T = unsafe { &mut *(ptr as *mut T) };
    let c_reader: &mut CPDReaderAPI = unsafe { &mut *(reader_api as *mut CPDReaderAPI) };
    let c_writer: &mut CPDWriterAPI = unsafe { &mut *(writer_api as *mut CPDWriterAPI) };
    let mut reader = Reader {
        api: c_reader,
        it: 0,
    };

    let mut writer = Writer { api: c_writer };

    backend.update(action as i32, &mut reader, &mut writer);
}

#[macro_export]
macro_rules! define_backend_plugin {
    ($p_name:ident, $name:expr, $x:ty) => {
        static $p_name: CBackendCallbacks = CBackendCallbacks {
            name: $name as *const u8,
            create_instance: Some(prodbg_api::backend::create_backend_instance::<$x>),
            destroy_instance: Some(prodbg_api::backend::destroy_backend_instance::<$x>),
            register_menu: None,
            update: Some(prodbg_api::backend::update_backend_instance::<$x>)
        };
    }
}
<commit_msg>Support for menus in Rust API<commit_after>use read_write::*;
use service::*;
use libc::*;
//use std::os::raw;
use menu_service::{MenuFuncs, CMenuFuncs1};
use std::mem::transmute;

pub static BACKEND_API_VERSION: &'static [u8] = b"ProDBG Backend 1\0";

pub trait Backend {
    fn new(service: &Service) -> Self;
    fn update(&mut self, action: i32, reader: &mut Reader, writer: &mut Writer);
    fn register_menu(&mut self, menu_funcs: &MenuFuncs) -> *mut c_void;
}

pub type ServiceFunc = extern "C" fn(service: *const c_uchar) -> *mut c_void;

#[repr(C)]
pub struct CBackendCallbacks {
    pub name: *const c_uchar,
    pub create_instance: Option<fn(service_func: ServiceFunc) -> *mut c_void>,
    pub destroy_instance: Option<fn(*mut c_void)>,
    pub register_menu: Option<fn(ptr: *mut c_void, menu_funcs: *mut c_void) -> *mut c_void>,
    pub update: Option<fn(ptr: *mut c_void,
                          a: c_int,
                          ra: *mut c_void,
                          wa: *mut c_void)>,
}

unsafe impl Sync for CBackendCallbacks {}

pub fn create_backend_instance<T: Backend>(service_func: extern "C" fn(service: *const c_uchar)
                                                                       -> *mut c_void)
                                           -> *mut c_void {
    let service = Service { service_func: service_func };
    let instance = unsafe { transmute(Box::new(T::new(&service))) };
    println!("Lets create instance!");
    instance
}

pub fn destroy_backend_instance<T: Backend>(ptr: *mut c_void) {
    println!("rust: backend: destroy");
    let _: Box<T> = unsafe { transmute(ptr) };
    // implicitly dropped
}

pub fn update_backend_instance<T: Backend>(ptr: *mut c_void,
                                           action: c_int,
                                           reader_api: *mut c_void,
                                           writer_api: *mut c_void) {
    let backend: &mut T = unsafe { &mut *(ptr as *mut T) };
    let c_reader: &mut CPDReaderAPI = unsafe { &mut *(reader_api as *mut CPDReaderAPI) };
    let c_writer: &mut CPDWriterAPI = unsafe { &mut *(writer_api as *mut CPDWriterAPI) };
    let mut reader = Reader {
        api: c_reader,
        it: 0,
    };

    let mut writer = Writer { api: c_writer };

    backend.update(action as i32, &mut reader, &mut writer);
}

pub fn register_backend_menu<T: Backend>(ptr: *mut c_void,
                                         menu_api: *mut c_void) -> *mut c_void {
    let backend: &mut T = unsafe { &mut *(ptr as *mut T) };

    let mut menu_funcs = MenuFuncs {
        api: menu_api as *mut CMenuFuncs1,
    };

    backend.register_menu(&mut menu_funcs)
}

#[macro_export]
macro_rules! define_backend_plugin {
    ($p_name:ident, $name:expr, $x:ty) => {
        static $p_name: CBackendCallbacks = CBackendCallbacks {
            name: $name as *const u8,
            create_instance: Some(prodbg_api::backend::create_backend_instance::<$x>),
            destroy_instance: Some(prodbg_api::backend::destroy_backend_instance::<$x>),
            register_menu: Some(prodbg_api::backend::register_backend_menu::<$x>),
            update: Some(prodbg_api::backend::update_backend_instance::<$x>)
        };
    }
}
<|endoftext|>


<commit_before>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::analyzer;
use serde_reflection::{ContainerFormat, Format, Named, Registry, VariantFormat};
use std::collections::{BTreeMap, HashSet};
use std::io::{Result, Write};
use std::path::PathBuf;

pub fn output(
    out: &mut dyn Write,
    registry: &Registry,
    namespace: Option<&str>,
) -> std::result::Result<(), Box<dyn std::error::Error>> {
    output_preamble(out)?;
    output_open_namespace(out, namespace)?;

    let dependencies = analyzer::get_dependency_map(registry)?;
    let entries = analyzer::best_effort_topological_sort(&dependencies);
    let mut known_names = HashSet::new();
    let mut known_sizes = HashSet::new();
    for name in entries {
        for dependency in &dependencies[name] {
            if !known_names.contains(dependency) {
                output_container_forward_definition(out, *dependency)?;
                known_names.insert(*dependency);
            }
        }
        let format = &registry[name];
        output_container(out, name, format, &known_sizes, namespace)?;
        known_sizes.insert(name);
        known_names.insert(name);
    }

    output_close_namespace(out, namespace)?;
    writeln!(out)?;
    for (name, format) in registry {
        output_container_traits(out, &name, format, namespace)?;
    }
    Ok(())
}

fn output_preamble(out: &mut dyn std::io::Write) -> Result<()> {
    writeln!(
        out,
        r#"
#pragma once

#include "serde.hpp"
"#
    )
}

fn output_open_namespace(out: &mut dyn std::io::Write, namespace: Option<&str>) -> Result<()> {
    if let Some(name) = namespace {
        writeln!(out, "\nnamespace {} {{", name,)?;
    }
    Ok(())
}

fn output_close_namespace(out: &mut dyn std::io::Write, namespace: Option<&str>) -> Result<()> {
    if let Some(name) = namespace {
        writeln!(out, "}} // end of namespace {}", name,)?;
    }
    Ok(())
}

/// If known_sizes is present, we must try to return a type with a known size as well.
/// A non-empty `namespace_prefix` is required when the type is quoted from a nested struct.
fn quote_type(
    format: &Format,
    known_sizes: Option<&HashSet<&str>>,
    namespace_prefix: &str,
) -> String {
    use Format::*;
    match format {
        TypeName(x) => {
            if let Some(set) = known_sizes {
                if !set.contains(x.as_str()) {
                    // Cannot use unique_ptr because we need a copy constructor (e.g. for vectors).
                    return format!("std::shared_ptr<{}{}>", namespace_prefix, x);
                }
            }
            format!("{}{}", namespace_prefix, x)
        }
        Unit => "std::monostate".into(),
        Bool => "bool".into(),
        I8 => "int8_t".into(),
        I16 => "int16_t".into(),
        I32 => "int32_t".into(),
        I64 => "int64_t".into(),
        I128 => "serde::int128_t".into(),
        U8 => "uint8_t".into(),
        U16 => "uint16_t".into(),
        U32 => "uint32_t".into(),
        U64 => "uint64_t".into(),
        U128 => "serde::uint128_t".into(),
        F32 => "float".into(),
        F64 => "double".into(),
        Char => "char32_t".into(),
        Str => "std::string".into(),
        Bytes => "std::vector<uint8_t>".into(),

        Option(format) => format!(
            "std::optional<{}>",
            quote_type(format, known_sizes, namespace_prefix)
        ),
        Seq(format) => format!(
            "std::vector<{}>",
            quote_type(format, None, namespace_prefix)
        ),
        Map { key, value } => format!(
            "std::map<{}, {}>",
            quote_type(key, None, namespace_prefix),
            quote_type(value, None, namespace_prefix)
        ),
        Tuple(formats) => format!(
            "std::tuple<{}>",
            quote_types(formats, known_sizes, namespace_prefix)
        ),
        TupleArray { content, size } => format!(
            "std::array<{}, {}>",
            quote_type(content, known_sizes, namespace_prefix),
            *size
        ),

        Variable(_) => panic!("unexpected value"),
    }
}

fn quote_types(
    formats: &[Format],
    known_sizes: Option<&HashSet<&str>>,
    namespace_prefix: &str,
) -> String {
    formats
        .iter()
        .map(|x| quote_type(x, known_sizes, namespace_prefix))
        .collect::<Vec<_>>()
        .join(", ")
}

fn output_fields(
    out: &mut dyn std::io::Write,
    indentation: usize,
    fields: &[Named<Format>],
    known_sizes: &HashSet<&str>,
    namespace_prefix: &str,
) -> Result<()> {
    let tab = " ".repeat(indentation);
    for field in fields {
        writeln!(
            out,
            "{}{} {};",
            tab,
            quote_type(&field.value, Some(known_sizes), namespace_prefix),
            field.name
        )?;
    }
    Ok(())
}

fn output_variant(
    out: &mut dyn std::io::Write,
    name: &str,
    variant: &VariantFormat,
    known_sizes: &HashSet<&str>,
    namespace: Option<&str>,
) -> Result<()> {
    use VariantFormat::*;
    let operator = format!("friend bool operator==(const {}&, const {}&);", name, name);
    let namespace_prefix = match namespace {
        None => "::".into(),
        Some(name) => format!("{}::", name),
    };
    match variant {
        Unit => writeln!(out, "    struct {} {{\n        {}\n    }};", name, operator),
        NewType(format) => writeln!(
            out,
            "    struct {} {{\n        {} value;\n        {}\n    }};",
            name,
            quote_type(format, Some(known_sizes), &namespace_prefix),
            operator,
        ),
        Tuple(formats) => writeln!(
            out,
            "    struct {} {{\n        std::tuple<{}> value;\n        {}\n    }};",
            name,
            quote_types(formats, Some(known_sizes), &namespace_prefix),
            operator
        ),
        Struct(fields) => {
            writeln!(out, "    struct {} {{", name)?;
            output_fields(out, 8, fields, known_sizes, &namespace_prefix)?;
            writeln!(out, "        {}\n    }};", operator)
        }
        Variable(_) => panic!("incorrect value"),
    }
}

fn output_variants(
    out: &mut dyn std::io::Write,
    variants: &BTreeMap<u32, Named<VariantFormat>>,
    known_sizes: &HashSet<&str>,
    namespace: Option<&str>,
) -> Result<()> {
    for (expected_index, (index, variant)) in variants.iter().enumerate() {
        assert_eq!(*index, expected_index as u32);
        output_variant(out, &variant.name, &variant.value, known_sizes, namespace)?;
    }
    Ok(())
}

fn output_container_forward_definition(out: &mut dyn std::io::Write, name: &str) -> Result<()> {
    writeln!(out, "struct {};\n", name)
}

fn output_container(
    out: &mut dyn std::io::Write,
    name: &str,
    format: &ContainerFormat,
    known_sizes: &HashSet<&str>,
    namespace: Option<&str>,
) -> Result<()> {
    use ContainerFormat::*;
    let operator = format!("friend bool operator==(const {}&, const {}&);", name, name);
    match format {
        UnitStruct => writeln!(out, "struct {} {{\n    {}\n}};\n", name, operator),
        NewTypeStruct(format) => writeln!(
            out,
            "struct {} {{\n    {} value;\n    {}\n}};\n",
            name,
            quote_type(format, Some(known_sizes), ""),
            operator,
        ),
        TupleStruct(formats) => writeln!(
            out,
            "struct {} {{\n    std::tuple<{}> value;\n    {}\n}};\n",
            name,
            quote_types(formats, Some(known_sizes), ""),
            operator,
        ),
        Struct(fields) => {
            writeln!(out, "struct {} {{", name)?;
            output_fields(out, 4, fields, known_sizes, "")?;
            writeln!(out, "    {}\n}};\n", operator)
        }
        Enum(variants) => {
            writeln!(out, "struct {} {{", name)?;
            output_variants(out, variants, known_sizes, namespace)?;
            writeln!(
                out,
                "    std::variant<{}> value;\n    {}\n}};\n",
                variants
                    .iter()
                    .map(|(_, v)| v.name.clone())
                    .collect::<Vec<_>>()
                    .join(", "),
                operator,
            )
        }
    }
}

fn output_struct_equality_test(
    out: &mut dyn std::io::Write,
    name: &str,
    fields: &[&str],
) -> Result<()> {
    writeln!(
        out,
        "inline bool operator==(const {} &lhs, const {} &rhs) {{",
        name, name,
    )?;
    for field in fields {
        writeln!(
            out,
            "    if (!(lhs.{} == rhs.{})) {{ return false; }}",
            field, field,
        )?;
    }
    writeln!(out, "    return true;\n}}")
}

fn output_struct_serializable(
    out: &mut dyn std::io::Write,
    name: &str,
    fields: &[&str],
) -> Result<()> {
    writeln!(
        out,
        r#"
template <>
template <typename Serializer>
void serde::Serializable<{}>::serialize(const {} &obj, Serializer &serializer) {{"#,
        name, name,
    )?;
    for field in fields {
        writeln!(
            out,
            "    serde::Serializable<decltype(obj.{})>::serialize(obj.{}, serializer);",
            field, field,
        )?;
    }
    writeln!(out, "}}")
}

fn output_struct_deserializable(
    out: &mut dyn std::io::Write,
    name: &str,
    fields: &[&str],
) -> Result<()> {
    writeln!(
        out,
        r#"
template <>
template <typename Deserializer>
{} serde::Deserializable<{}>::deserialize(Deserializer &deserializer) {{
    {} obj;"#,
        name, name, name,
    )?;
    for field in fields {
        writeln!(
            out,
            "    obj.{} = serde::Deserializable<decltype(obj.{})>::deserialize(deserializer);",
            field, field,
        )?;
    }
    writeln!(out, "    return obj;\n}}")
}

fn output_struct_traits(
    out: &mut dyn std::io::Write,
    name: &str,
    fields: &[&str],
    namespace: Option<&str>,
) -> Result<()> {
    output_open_namespace(out, namespace)?;
    output_struct_equality_test(out, name, fields)?;
    output_close_namespace(out, namespace)?;
    let namespaced_name = match namespace {
        None => name.into(),
        Some(prefix) => format!("{}::{}", prefix, name),
    };
    output_struct_serializable(out, &namespaced_name, fields)?;
    output_struct_deserializable(out, &namespaced_name, fields)
}

fn get_variant_fields(format: &VariantFormat) -> Vec<&str> {
    use VariantFormat::*;
    match format {
        Unit => Vec::new(),
        NewType(_format) => vec!["value"],
        Tuple(_formats) => vec!["value"],
        Struct(fields) => fields
            .iter()
            .map(|field| field.name.as_str())
            .collect::<Vec<_>>(),
        Variable(_) => panic!("incorrect value"),
    }
}

fn output_container_traits(
    out: &mut dyn std::io::Write,
    name: &str,
    format: &ContainerFormat,
    namespace: Option<&str>,
) -> Result<()> {
    use ContainerFormat::*;
    match format {
        UnitStruct => output_struct_traits(out, name, &[], namespace),
        NewTypeStruct(_format) => output_struct_traits(out, name, &["value"], namespace),
        TupleStruct(_formats) => output_struct_traits(out, name, &["value"], namespace),
        Struct(fields) => output_struct_traits(
            out,
            name,
            &fields
                .iter()
                .map(|field| field.name.as_str())
                .collect::<Vec<_>>(),
            namespace,
        ),
        Enum(variants) => {
            output_struct_traits(out, name, &["value"], namespace)?;
            for variant in variants.values() {
                output_struct_traits(
                    out,
                    &format!("{}::{}", name, variant.name),
                    &get_variant_fields(&variant.value),
                    namespace,
                )?;
            }
            Ok(())
        }
    }
}

pub struct Installer {
    install_dir: PathBuf,
}

impl Installer {
    pub fn new(install_dir: PathBuf) -> Self {
        Installer { install_dir }
    }

    fn create_header_file(&self, name: &str) -> Result<std::fs::File> {
        let dir_path = &self.install_dir;
        std::fs::create_dir_all(dir_path)?;
        std::fs::File::create(dir_path.join(name.to_string() + ".hpp"))
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_module(
        &self,
        name: &str,
        registry: &Registry,
    ) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file(name)?;
        output(&mut file, &registry, Some(name))
    }

    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file("serde")?;
        write!(file, "{}", include_str!("../runtime/cpp/serde.hpp"))?;
        Ok(())
    }

    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file("bincode")?;
        write!(file, "{}", include_str!("../runtime/cpp/bincode.hpp"))?;
        Ok(())
    }

    fn install_lcs_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file("lcs")?;
        write!(file, "{}", include_str!("../runtime/cpp/lcs.hpp"))?;
        Ok(())
    }
}
<commit_msg>[serde generate] c++: add CppEmitter<commit_after>// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::analyzer;
use serde_reflection::{ContainerFormat, Format, Named, Registry, VariantFormat};
use std::collections::{BTreeMap, HashSet};
use std::io::{Result, Write};
use std::path::PathBuf;

pub fn output(
    out: &mut dyn Write,
    registry: &Registry,
    namespace: Option<&str>,
) -> std::result::Result<(), Box<dyn std::error::Error>> {
    let namespace_prefix = match namespace {
        None => "::".to_string(),
        Some(name) => format!("{}::", name),
    };
    let mut emitter = CppEmitter {
        out,
        indentation: 0,
        namespace,
        namespace_prefix,
        known_names: HashSet::new(),
        known_sizes: HashSet::new(),
    };

    emitter.output_preamble()?;
    emitter.output_open_namespace()?;

    let dependencies = analyzer::get_dependency_map(registry)?;
    let entries = analyzer::best_effort_topological_sort(&dependencies);

    for name in entries {
        for dependency in &dependencies[name] {
            if !emitter.known_names.contains(dependency) {
                emitter.output_container_forward_definition(*dependency)?;
                emitter.known_names.insert(*dependency);
            }
        }
        let format = &registry[name];
        emitter.output_container(name, format)?;
        emitter.known_sizes.insert(name);
        emitter.known_names.insert(name);
    }

    emitter.output_close_namespace()?;
    writeln!(emitter.out)?;
    for (name, format) in registry {
        emitter.output_container_traits(&name, format)?;
    }
    Ok(())
}

struct CppEmitter<'a, T> {
    out: T,
    indentation: usize,
    namespace: Option<&'a str>,
    namespace_prefix: String,
    known_names: HashSet<&'a str>,
    known_sizes: HashSet<&'a str>,
}

impl<'a, T> CppEmitter<'a, T>
where
    T: std::io::Write,
{
    fn output_preamble(&mut self) -> Result<()> {
        writeln!(
            self.out,
            r#"
#pragma once

#include "serde.hpp"
"#
        )
    }

    fn output_open_namespace(&mut self) -> Result<()> {
        assert_eq!(self.indentation, 0);
        if let Some(name) = self.namespace {
            writeln!(self.out, "\nnamespace {} {{\n", name,)?;
        }
        Ok(())
    }

    fn output_close_namespace(&mut self) -> Result<()> {
        assert_eq!(self.indentation, 0);
        if let Some(name) = self.namespace {
            writeln!(self.out, "\n}} // end of namespace {}", name,)?;
        }
        Ok(())
    }

    /// If known_sizes is present, we must try to return a type with a known size as well.
    /// A non-empty `namespace_prefix` is required when the type is quoted from a nested struct.
    fn quote_type(
        format: &Format,
        known_sizes: Option<&HashSet<&str>>,
        namespace_prefix: &str,
    ) -> String {
        use Format::*;
        match format {
            TypeName(x) => {
                if let Some(set) = known_sizes {
                    if !set.contains(x.as_str()) {
                        // Cannot use unique_ptr because we need a copy constructor (e.g. for vectors).
                        return format!("std::shared_ptr<{}{}>", namespace_prefix, x);
                    }
                }
                format!("{}{}", namespace_prefix, x)
            }
            Unit => "std::monostate".into(),
            Bool => "bool".into(),
            I8 => "int8_t".into(),
            I16 => "int16_t".into(),
            I32 => "int32_t".into(),
            I64 => "int64_t".into(),
            I128 => "serde::int128_t".into(),
            U8 => "uint8_t".into(),
            U16 => "uint16_t".into(),
            U32 => "uint32_t".into(),
            U64 => "uint64_t".into(),
            U128 => "serde::uint128_t".into(),
            F32 => "float".into(),
            F64 => "double".into(),
            Char => "char32_t".into(),
            Str => "std::string".into(),
            Bytes => "std::vector<uint8_t>".into(),

            Option(format) => format!(
                "std::optional<{}>",
                Self::quote_type(format, known_sizes, namespace_prefix)
            ),
            Seq(format) => format!(
                "std::vector<{}>",
                Self::quote_type(format, None, namespace_prefix)
            ),
            Map { key, value } => format!(
                "std::map<{}, {}>",
                Self::quote_type(key, None, namespace_prefix),
                Self::quote_type(value, None, namespace_prefix)
            ),
            Tuple(formats) => format!(
                "std::tuple<{}>",
                Self::quote_types(formats, known_sizes, namespace_prefix)
            ),
            TupleArray { content, size } => format!(
                "std::array<{}, {}>",
                Self::quote_type(content, known_sizes, namespace_prefix),
                *size
            ),

            Variable(_) => panic!("unexpected value"),
        }
    }

    fn quote_types(
        formats: &[Format],
        known_sizes: Option<&HashSet<&str>>,
        namespace_prefix: &str,
    ) -> String {
        formats
            .iter()
            .map(|x| Self::quote_type(x, known_sizes, namespace_prefix))
            .collect::<Vec<_>>()
            .join(", ")
    }

    fn output_fields(&mut self, fields: &[Named<Format>]) -> Result<()> {
        self.indentation += 1;
        let tab = "    ".repeat(self.indentation);
        for field in fields {
            writeln!(
                self.out,
                "{}{} {};",
                tab,
                Self::quote_type(
                    &field.value,
                    Some(&self.known_sizes),
                    &self.namespace_prefix
                ),
                field.name
            )?;
        }
        self.indentation -= 1;
        Ok(())
    }

    fn output_variant(&mut self, name: &str, variant: &VariantFormat) -> Result<()> {
        assert_eq!(self.indentation, 1);
        use VariantFormat::*;
        let operator = format!("friend bool operator==(const {}&, const {}&);", name, name);
        match variant {
            Unit => writeln!(
                self.out,
                "    struct {} {{\n        {}\n    }};",
                name, operator
            ),
            NewType(format) => writeln!(
                self.out,
                "    struct {} {{\n        {} value;\n        {}\n    }};",
                name,
                Self::quote_type(format, Some(&self.known_sizes), &self.namespace_prefix),
                operator,
            ),
            Tuple(formats) => writeln!(
                self.out,
                "    struct {} {{\n        std::tuple<{}> value;\n        {}\n    }};",
                name,
                Self::quote_types(formats, Some(&self.known_sizes), &self.namespace_prefix),
                operator
            ),
            Struct(fields) => {
                writeln!(self.out, "    struct {} {{", name)?;
                self.output_fields(fields)?;
                writeln!(self.out, "        {}\n    }};", operator)
            }
            Variable(_) => panic!("incorrect value"),
        }
    }

    fn output_variants(&mut self, variants: &BTreeMap<u32, Named<VariantFormat>>) -> Result<()> {
        assert_eq!(self.indentation, 0);
        self.indentation += 1;
        for (expected_index, (index, variant)) in variants.iter().enumerate() {
            assert_eq!(*index, expected_index as u32);
            self.output_variant(&variant.name, &variant.value)?;
        }
        self.indentation -= 1;
        Ok(())
    }

    fn output_container_forward_definition(&mut self, name: &str) -> Result<()> {
        writeln!(self.out, "struct {};\n", name)
    }

    fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
        assert_eq!(self.indentation, 0);
        use ContainerFormat::*;
        let operator = format!("friend bool operator==(const {}&, const {}&);", name, name);
        match format {
            UnitStruct => writeln!(self.out, "struct {} {{\n    {}\n}};\n", name, operator),
            NewTypeStruct(format) => writeln!(
                self.out,
                "struct {} {{\n    {} value;\n    {}\n}};\n",
                name,
                Self::quote_type(format, Some(&self.known_sizes), ""),
                operator,
            ),
            TupleStruct(formats) => writeln!(
                self.out,
                "struct {} {{\n    std::tuple<{}> value;\n    {}\n}};\n",
                name,
                Self::quote_types(formats, Some(&self.known_sizes), ""),
                operator,
            ),
            Struct(fields) => {
                writeln!(self.out, "struct {} {{", name)?;
                self.output_fields(fields)?;
                writeln!(self.out, "    {}\n}};\n", operator)
            }
            Enum(variants) => {
                writeln!(self.out, "struct {} {{", name)?;
                self.output_variants(variants)?;
                writeln!(
                    self.out,
                    "    std::variant<{}> value;\n    {}\n}};\n",
                    variants
                        .iter()
                        .map(|(_, v)| v.name.clone())
                        .collect::<Vec<_>>()
                        .join(", "),
                    operator,
                )
            }
        }
    }

    fn output_struct_equality_test(&mut self, name: &str, fields: &[&str]) -> Result<()> {
        assert_eq!(self.indentation, 0);
        writeln!(
            self.out,
            "inline bool operator==(const {} &lhs, const {} &rhs) {{",
            name, name,
        )?;
        for field in fields {
            writeln!(
                self.out,
                "    if (!(lhs.{} == rhs.{})) {{ return false; }}",
                field, field,
            )?;
        }
        writeln!(self.out, "    return true;\n}}")
    }

    fn output_struct_serializable(&mut self, name: &str, fields: &[&str]) -> Result<()> {
        assert_eq!(self.indentation, 0);
        writeln!(
            self.out,
            r#"
template <>
template <typename Serializer>
void serde::Serializable<{}>::serialize(const {} &obj, Serializer &serializer) {{"#,
            name, name,
        )?;
        for field in fields {
            writeln!(
                self.out,
                "    serde::Serializable<decltype(obj.{})>::serialize(obj.{}, serializer);",
                field, field,
            )?;
        }
        writeln!(self.out, "}}")
    }

    fn output_struct_deserializable(&mut self, name: &str, fields: &[&str]) -> Result<()> {
        assert_eq!(self.indentation, 0);
        writeln!(
            self.out,
            r#"
template <>
template <typename Deserializer>
{} serde::Deserializable<{}>::deserialize(Deserializer &deserializer) {{
    {} obj;"#,
            name, name, name,
        )?;
        for field in fields {
            writeln!(
                self.out,
                "    obj.{} = serde::Deserializable<decltype(obj.{})>::deserialize(deserializer);",
                field, field,
            )?;
        }
        writeln!(self.out, "    return obj;\n}}")
    }

    fn output_struct_traits(&mut self, name: &str, fields: &[&str]) -> Result<()> {
        assert_eq!(self.indentation, 0);
        self.output_open_namespace()?;
        self.output_struct_equality_test(name, fields)?;
        self.output_close_namespace()?;
        let namespaced_name = format!("{}{}", self.namespace_prefix, name);
        self.output_struct_serializable(&namespaced_name, fields)?;
        self.output_struct_deserializable(&namespaced_name, fields)
    }

    fn get_variant_fields(format: &VariantFormat) -> Vec<&str> {
        use VariantFormat::*;
        match format {
            Unit => Vec::new(),
            NewType(_format) => vec!["value"],
            Tuple(_formats) => vec!["value"],
            Struct(fields) => fields
                .iter()
                .map(|field| field.name.as_str())
                .collect::<Vec<_>>(),
            Variable(_) => panic!("incorrect value"),
        }
    }

    fn output_container_traits(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
        assert_eq!(self.indentation, 0);
        use ContainerFormat::*;
        match format {
            UnitStruct => self.output_struct_traits(name, &[]),
            NewTypeStruct(_format) => self.output_struct_traits(name, &["value"]),
            TupleStruct(_formats) => self.output_struct_traits(name, &["value"]),
            Struct(fields) => self.output_struct_traits(
                name,
                &fields
                    .iter()
                    .map(|field| field.name.as_str())
                    .collect::<Vec<_>>(),
            ),
            Enum(variants) => {
                self.output_struct_traits(name, &["value"])?;
                for variant in variants.values() {
                    self.output_struct_traits(
                        &format!("{}::{}", name, variant.name),
                        &Self::get_variant_fields(&variant.value),
                    )?;
                }
                Ok(())
            }
        }
    }
}

pub struct Installer {
    install_dir: PathBuf,
}

impl Installer {
    pub fn new(install_dir: PathBuf) -> Self {
        Installer { install_dir }
    }

    fn create_header_file(&self, name: &str) -> Result<std::fs::File> {
        let dir_path = &self.install_dir;
        std::fs::create_dir_all(dir_path)?;
        std::fs::File::create(dir_path.join(name.to_string() + ".hpp"))
    }
}

impl crate::SourceInstaller for Installer {
    type Error = Box<dyn std::error::Error>;

    fn install_module(
        &self,
        name: &str,
        registry: &Registry,
    ) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file(name)?;
        output(&mut file, &registry, Some(name))
    }

    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file("serde")?;
        write!(file, "{}", include_str!("../runtime/cpp/serde.hpp"))?;
        Ok(())
    }

    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file("bincode")?;
        write!(file, "{}", include_str!("../runtime/cpp/bincode.hpp"))?;
        Ok(())
    }

    fn install_lcs_runtime(&self) -> std::result::Result<(), Self::Error> {
        let mut file = self.create_header_file("lcs")?;
        write!(file, "{}", include_str!("../runtime/cpp/lcs.hpp"))?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate regex;

use clap::{App, Arg};
use regex::Regex;
use std::fs::File;
//use std::io;
use std::io::prelude::*;
use std::io::BufReader;

fn process_lines<T: BufRead>(reader: T, re: Regex) {
    for line_ in reader.lines() {
        //let line = line_.unwrap();
        match line_ {
            Ok(line) => match re.find(&line) {
                Some(_) => println!("{}", line),
                None => (),
            },
            _ => (),
        }
    }
}

fn main() {
    let args = App::new("grep-lite")
        .version("0.1")
        .about("searches for patterns")
        .arg(
            Arg::with_name("pattern")
                .help("The pattern to search for")
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name("input")
                .help("File to search")
                .takes_value(true)
                .required(false),
        )
        .get_matches();

    let pattern = args.value_of("pattern").unwrap();
    let re = Regex::new(pattern).unwrap();

    let input = args.value_of("input").unwrap_or("-");

    if input == "-" {
        let stdin = std::io::stdin();
        let reader = stdin.lock();
        process_lines(reader, re);
    } else {
        let f = File::open(input).unwrap();
        let reader = BufReader::new(f);
        process_lines(reader, re);
    }

    // functional approach
    //quote.lines().filter(|l| re.is_match(l)).map(|m| println!("{}", m));
}
<commit_msg>fix error checking<commit_after>extern crate clap;
extern crate regex;

use clap::{App, Arg};
use regex::Regex;
use std::fs::File;
//use std::io;
use std::io::prelude::*;
use std::io::BufReader;

fn process_lines<T: BufRead>(reader: T, re: Regex) {
    for line_ in reader.lines() {
        //let line = line_.unwrap();
        match line_ {
            Ok(line) => match re.find(&line) {
                Some(_) => println!("{}", line),
                None => (),
            },
            Err(e) => panic!(e),
        }
    }
}

fn main() {
    let args = App::new("grep-lite")
        .version("0.1")
        .about("searches for patterns")
        .arg(
            Arg::with_name("pattern")
                .help("The pattern to search for")
                .takes_value(true)
                .required(true),
        )
        .arg(
            Arg::with_name("input")
                .help("File to search")
                .takes_value(true)
                .required(false),
        )
        .get_matches();

    let pattern = args.value_of("pattern").unwrap();
    let re = Regex::new(pattern).unwrap();

    let input = args.value_of("input").unwrap_or("-");

    if input == "-" {
        let stdin = std::io::stdin();
        let reader = stdin.lock();
        process_lines(reader, re);
    } else {
        let f = File::open(input).unwrap();
        let reader = BufReader::new(f);
        process_lines(reader, re);
    }

    // functional approach
    //quote.lines().filter(|l| re.is_match(l)).map(|m| println!("{}", m));
}
<|endoftext|>


<commit_before>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn, TOut: Serialize, TErr: Serialize>
where for<'de> TIn: Deserialize<'de> {
    function: Box<Fn(TIn, &Environment) -> Result<TOut, TErr> + Send + Sync>
}

///
/// Creates a Tool from a function that can produce an error and uses an environment
///
pub fn make_dynamic_tool<TIn, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn, &Environment) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr>
where for<'de> TIn: Deserialize<'de> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr>
where for<'de> TIn: Deserialize<'de> {
    make_dynamic_tool(move |input, _| function(input))
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn, TOut: Serialize, F: 'static+Send+Sync+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()>
where for<'de> TIn: Deserialize<'de> {
    make_dynamic_tool(move |input, _| Ok(function(input)))
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where for<'de> TIn: Deserialize<'de>, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded, environment);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut>
where for<'de> TOut: Deserialize<'de> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut> TypedTool<TIn, TOut>
where for<'de> TOut: Deserialize<'de> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<TIn: Serialize, TOut>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>
    where for<'de> TOut: Deserialize<'de>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<TIn: Serialize, TOut>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>
    where for<'de> TOut: Deserialize<'de> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }
}
<commit_msg>Found that our dynamic tools weren’t producing errors properly, add a test to verify this<commit_after>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn, TOut: Serialize, TErr: Serialize>
where for<'de> TIn: Deserialize<'de> {
    function: Box<Fn(TIn, &Environment) -> Result<TOut, TErr> + Send + Sync>
}

///
/// Creates a Tool from a function that can produce an error and uses an environment
///
pub fn make_dynamic_tool<TIn, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn, &Environment) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr>
where for<'de> TIn: Deserialize<'de> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr>
where for<'de> TIn: Deserialize<'de> {
    make_dynamic_tool(move |input, _| function(input))
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn, TOut: Serialize, F: 'static+Send+Sync+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()>
where for<'de> TIn: Deserialize<'de> {
    make_dynamic_tool(move |input, _| Ok(function(input)))
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where for<'de> TIn: Deserialize<'de>, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded, environment);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut>
where for<'de> TOut: Deserialize<'de> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut> TypedTool<TIn, TOut>
where for<'de> TOut: Deserialize<'de> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<TIn: Serialize, TOut>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>
    where for<'de> TOut: Deserialize<'de>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<TIn: Serialize, TOut>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>
    where for<'de> TOut: Deserialize<'de> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }

    #[test]
    fn can_call_dynamic_tool() {
        let tool            = make_dynamic_tool(|x: i32, _env: &Environment| -> Result<i32, Value> { Ok(x+1) });
        let environment     = EmptyEnvironment::new();

        let should_be_error = tool.invoke_json(json![ 1 ], &environment);
        assert!(should_be_error == Ok(json![2]));
    }

    #[test]
    fn dynamic_tool_can_produce_error() {
        let tool            = make_dynamic_tool(|_: i32, _env: &Environment| -> Result<i32, Value> { Err(json![ "Oops" ]) });
        let environment     = EmptyEnvironment::new();

        let should_be_error = tool.invoke_json(json![ 1 ], &environment);
        assert!(should_be_error == Err(json!["Oops"]));
    }
}
<|endoftext|>