<commit_before>//! Driver for rust-analyzer.
//!
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
mod flags;
mod logger;

use std::{convert::TryFrom, env, fs, path::Path, process};

use lsp_server::Connection;
use project_model::ProjectManifest;
use rust_analyzer::{
    cli::{self, AnalysisStatsCmd},
    config::Config,
    from_json,
    lsp_ext::supports_utf8,
    Result,
};
use vfs::AbsPathBuf;

#[cfg(all(feature = "mimalloc"))]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn main() {
    if let Err(err) = try_main() {
        log::error!("Unexpected error: {}", err);
        eprintln!("{}", err);
        process::exit(101);
    }
}

fn try_main() -> Result<()> {
    let flags = flags::RustAnalyzer::from_env()?;

    #[cfg(debug_assertions)]
    if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
        #[allow(unused_mut)]
        let mut d = 4;
        while d == 4 {
            d = 4;
        }
    }

    setup_logging(flags.log_file.as_deref(), flags.no_log_buffering)?;
    let verbosity = flags.verbosity();

    match flags.subcommand {
        flags::RustAnalyzerCmd::LspServer(cmd) => {
            if cmd.print_config_schema {
                println!("{:#}", Config::json_schema());
                return Ok(());
            }
            if cmd.version {
                println!("rust-analyzer {}", env!("REV"));
                return Ok(());
            }
            if cmd.help {
                println!("{}", flags::RustAnalyzer::HELP);
                return Ok(());
            }
            run_server()?
        }
        flags::RustAnalyzerCmd::ProcMacro(_) => proc_macro_srv::cli::run()?,
        flags::RustAnalyzerCmd::Parse(cmd) => cli::parse(cmd.no_dump)?,
        flags::RustAnalyzerCmd::Symbols(_) => cli::symbols()?,
        flags::RustAnalyzerCmd::Highlight(cmd) => cli::highlight(cmd.rainbow)?,
        flags::RustAnalyzerCmd::AnalysisStats(cmd) => AnalysisStatsCmd {
            randomize: cmd.randomize,
            parallel: cmd.parallel,
            memory_usage: cmd.memory_usage,
            only: cmd.only,
            with_deps: cmd.with_deps,
            no_sysroot: cmd.no_sysroot,
            path: cmd.path,
            load_output_dirs: cmd.load_output_dirs,
            with_proc_macro: cmd.with_proc_macro,
        }
        .run(verbosity)?,

        flags::RustAnalyzerCmd::Diagnostics(cmd) => {
            cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
        }
        flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?,
        flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?,
    }
    Ok(())
}

fn setup_logging(log_file: Option<&Path>, no_buffering: bool) -> Result<()> {
    env::set_var("RUST_BACKTRACE", "short");

    let log_file = match log_file {
        Some(path) => {
            if let Some(parent) = path.parent() {
                let _ = fs::create_dir_all(parent);
            }
            Some(fs::File::create(path)?)
        }
        None => None,
    };
    let filter = env::var("RA_LOG").ok();
    logger::Logger::new(log_file, no_buffering, filter.as_deref()).install();

    tracing_setup::setup_tracing()?;

    profile::init();

    Ok(())
}

mod tracing_setup {
    use tracing::subscriber;
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::EnvFilter;
    use tracing_subscriber::Registry;
    use tracing_tree::HierarchicalLayer;

    pub(crate) fn setup_tracing() -> super::Result<()> {
        let filter = EnvFilter::from_env("CHALK_DEBUG");
        let layer = HierarchicalLayer::default()
            .with_indent_lines(true)
            .with_ansi(false)
            .with_indent_amount(2)
            .with_writer(std::io::stderr);
        let subscriber = Registry::default().with(filter).with(layer);
        subscriber::set_global_default(subscriber)?;
        Ok(())
    }
}

fn run_server() -> Result<()> {
    log::info!("server will start");

    let (connection, io_threads) = Connection::stdio();

    let (initialize_id, initialize_params) = connection.initialize_start()?;
    log::info!("InitializeParams: {}", initialize_params);
    let initialize_params =
        from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;

    let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);

    let initialize_result = lsp_types::InitializeResult {
        capabilities: server_capabilities,
        server_info: Some(lsp_types::ServerInfo {
            name: String::from("rust-analyzer"),
            version: Some(String::from(env!("REV"))),
        }),
        offset_encoding: if supports_utf8(&initialize_params.capabilities) {
            Some("utf-8".to_string())
        } else {
            None
        },
    };

    let initialize_result = serde_json::to_value(initialize_result).unwrap();

    connection.initialize_finish(initialize_id, initialize_result)?;

    if let Some(client_info) = initialize_params.client_info {
        log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
    }

    let config = {
        let root_path = match initialize_params
            .root_uri
            .and_then(|it| it.to_file_path().ok())
            .and_then(|it| AbsPathBuf::try_from(it).ok())
        {
            Some(it) => it,
            None => {
                let cwd = env::current_dir()?;
                AbsPathBuf::assert(cwd)
            }
        };

        let mut config = Config::new(root_path, initialize_params.capabilities);
        if let Some(json) = initialize_params.initialization_options {
            config.update(json);
        }

        if config.linked_projects().is_empty() {
            let workspace_roots = initialize_params
                .workspace_folders
                .map(|workspaces| {
                    workspaces
                        .into_iter()
                        .filter_map(|it| it.uri.to_file_path().ok())
                        .filter_map(|it| AbsPathBuf::try_from(it).ok())
                        .collect::<Vec<_>>()
                })
                .filter(|workspaces| !workspaces.is_empty())
                .unwrap_or_else(|| vec![config.root_path.clone()]);

            let discovered = ProjectManifest::discover_all(&workspace_roots);
            log::info!("discovered projects: {:?}", discovered);
            if discovered.is_empty() {
                log::error!("failed to find any projects in {:?}", workspace_roots);
            }

            config.discovered_projects = Some(discovered);
        }

        config
    };

    rust_analyzer::main_loop(config, connection)?;

    io_threads.join()?;
    log::info!("server did shut down");
    Ok(())
}
<commit_msg>Put the revision in the logged startup message<commit_after>//! Driver for rust-analyzer.
//!
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
mod flags;
mod logger;

use std::{convert::TryFrom, env, fs, path::Path, process};

use lsp_server::Connection;
use project_model::ProjectManifest;
use rust_analyzer::{
    cli::{self, AnalysisStatsCmd},
    config::Config,
    from_json,
    lsp_ext::supports_utf8,
    Result,
};
use vfs::AbsPathBuf;

#[cfg(all(feature = "mimalloc"))]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn main() {
    if let Err(err) = try_main() {
        log::error!("Unexpected error: {}", err);
        eprintln!("{}", err);
        process::exit(101);
    }
}

fn try_main() -> Result<()> {
    let flags = flags::RustAnalyzer::from_env()?;

    #[cfg(debug_assertions)]
    if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
        #[allow(unused_mut)]
        let mut d = 4;
        while d == 4 {
            d = 4;
        }
    }

    setup_logging(flags.log_file.as_deref(), flags.no_log_buffering)?;
    let verbosity = flags.verbosity();

    match flags.subcommand {
        flags::RustAnalyzerCmd::LspServer(cmd) => {
            if cmd.print_config_schema {
                println!("{:#}", Config::json_schema());
                return Ok(());
            }
            if cmd.version {
                println!("rust-analyzer {}", env!("REV"));
                return Ok(());
            }
            if cmd.help {
                println!("{}", flags::RustAnalyzer::HELP);
                return Ok(());
            }
            run_server()?
        }
        flags::RustAnalyzerCmd::ProcMacro(_) => proc_macro_srv::cli::run()?,
        flags::RustAnalyzerCmd::Parse(cmd) => cli::parse(cmd.no_dump)?,
        flags::RustAnalyzerCmd::Symbols(_) => cli::symbols()?,
        flags::RustAnalyzerCmd::Highlight(cmd) => cli::highlight(cmd.rainbow)?,
        flags::RustAnalyzerCmd::AnalysisStats(cmd) => AnalysisStatsCmd {
            randomize: cmd.randomize,
            parallel: cmd.parallel,
            memory_usage: cmd.memory_usage,
            only: cmd.only,
            with_deps: cmd.with_deps,
            no_sysroot: cmd.no_sysroot,
            path: cmd.path,
            load_output_dirs: cmd.load_output_dirs,
            with_proc_macro: cmd.with_proc_macro,
        }
        .run(verbosity)?,

        flags::RustAnalyzerCmd::Diagnostics(cmd) => {
            cli::diagnostics(&cmd.path, cmd.load_output_dirs, cmd.with_proc_macro)?
        }
        flags::RustAnalyzerCmd::Ssr(cmd) => cli::apply_ssr_rules(cmd.rule)?,
        flags::RustAnalyzerCmd::Search(cmd) => cli::search_for_patterns(cmd.pattern, cmd.debug)?,
    }
    Ok(())
}

fn setup_logging(log_file: Option<&Path>, no_buffering: bool) -> Result<()> {
    env::set_var("RUST_BACKTRACE", "short");

    let log_file = match log_file {
        Some(path) => {
            if let Some(parent) = path.parent() {
                let _ = fs::create_dir_all(parent);
            }
            Some(fs::File::create(path)?)
        }
        None => None,
    };
    let filter = env::var("RA_LOG").ok();
    logger::Logger::new(log_file, no_buffering, filter.as_deref()).install();

    tracing_setup::setup_tracing()?;

    profile::init();

    Ok(())
}

mod tracing_setup {
    use tracing::subscriber;
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::EnvFilter;
    use tracing_subscriber::Registry;
    use tracing_tree::HierarchicalLayer;

    pub(crate) fn setup_tracing() -> super::Result<()> {
        let filter = EnvFilter::from_env("CHALK_DEBUG");
        let layer = HierarchicalLayer::default()
            .with_indent_lines(true)
            .with_ansi(false)
            .with_indent_amount(2)
            .with_writer(std::io::stderr);
        let subscriber = Registry::default().with(filter).with(layer);
        subscriber::set_global_default(subscriber)?;
        Ok(())
    }
}

fn run_server() -> Result<()> {
    log::info!("server version {} will start", env!("REV"));

    let (connection, io_threads) = Connection::stdio();

    let (initialize_id, initialize_params) = connection.initialize_start()?;
    log::info!("InitializeParams: {}", initialize_params);
    let initialize_params =
        from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;

    let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);

    let initialize_result = lsp_types::InitializeResult {
        capabilities: server_capabilities,
        server_info: Some(lsp_types::ServerInfo {
            name: String::from("rust-analyzer"),
            version: Some(String::from(env!("REV"))),
        }),
        offset_encoding: if supports_utf8(&initialize_params.capabilities) {
            Some("utf-8".to_string())
        } else {
            None
        },
    };

    let initialize_result = serde_json::to_value(initialize_result).unwrap();

    connection.initialize_finish(initialize_id, initialize_result)?;

    if let Some(client_info) = initialize_params.client_info {
        log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
    }

    let config = {
        let root_path = match initialize_params
            .root_uri
            .and_then(|it| it.to_file_path().ok())
            .and_then(|it| AbsPathBuf::try_from(it).ok())
        {
            Some(it) => it,
            None => {
                let cwd = env::current_dir()?;
                AbsPathBuf::assert(cwd)
            }
        };

        let mut config = Config::new(root_path, initialize_params.capabilities);
        if let Some(json) = initialize_params.initialization_options {
            config.update(json);
        }

        if config.linked_projects().is_empty() {
            let workspace_roots = initialize_params
                .workspace_folders
                .map(|workspaces| {
                    workspaces
                        .into_iter()
                        .filter_map(|it| it.uri.to_file_path().ok())
                        .filter_map(|it| AbsPathBuf::try_from(it).ok())
                        .collect::<Vec<_>>()
                })
                .filter(|workspaces| !workspaces.is_empty())
                .unwrap_or_else(|| vec![config.root_path.clone()]);

            let discovered = ProjectManifest::discover_all(&workspace_roots);
            log::info!("discovered projects: {:?}", discovered);
            if discovered.is_empty() {
                log::error!("failed to find any projects in {:?}", workspace_roots);
            }

            config.discovered_projects = Some(discovered);
        }

        config
    };

    rust_analyzer::main_loop(config, connection)?;

    io_threads.join()?;
    log::info!("server did shut down");
    Ok(())
}
<|endoftext|>


<commit_before>//! # Serde
//!
//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
//! structures efficiently and generically.
//!
//! The Serde ecosystem consists of data structures that know how to serialize
//! and deserialize themselves along with data formats that know how to
//! serialize and deserialize other things. Serde provides the layer by which
//! these two groups interact with each other, allowing any supported data
//! structure to be serialized and deserialized using any supported data format.
//!
//! See the Serde website <https://serde.rs/> for additional documentation and
//! usage examples.
//!
//! ## Design
//!
//! Where many other languages rely on runtime reflection for serializing data,
//! Serde is instead built on Rust's powerful trait system. A data structure
//! that knows how to serialize and deserialize itself is one that implements
//! Serde's `Serialize` and `Deserialize` traits (or uses Serde's derive
//! attribute to automatically generate implementations at compile time). This
//! avoids any overhead of reflection or runtime type information. In fact in
//! many situations the interaction between data structure and data format can
//! be completely optimized away by the Rust compiler, leaving Serde
//! serialization to perform the same speed as a handwritten serializer for the
//! specific selection of data structure and data format.
//!
//! ## Data formats
//!
//! The following is a partial list of data formats that have been implemented
//! for Serde by the community.
//!
//! - [JSON], the ubiquitous JavaScript Object Notation used by many HTTP APIs.
//! - [Bincode], a compact binary format
//!   used for IPC within the Servo rendering engine.
//! - [CBOR], a Concise Binary Object Representation designed for small message
//!   size without the need for version negotiation.
//! - [YAML], a self-proclaimed human-friendly configuration language that ain't
//!   markup language.
//! - [MessagePack], an efficient binary format that resembles a compact JSON.
//! - [TOML], a minimal configuration format used by [Cargo].
//! - [Pickle], a format common in the Python world.
//! - [RON], a Rusty Object Notation.
//! - [BSON], the data storage and network transfer format used by MongoDB.
//! - [Avro], a binary format used within Apache Hadoop, with support for schema
//!   definition.
//! - [JSON5], a superset of JSON including some productions from ES5.
//! - [Postcard], a no\_std and embedded-systems friendly compact binary format.
//! - [URL] query strings, in the x-www-form-urlencoded format.
//! - [Envy], a way to deserialize environment variables into Rust structs.
//!   *(deserialization only)*
//! - [Envy Store], a way to deserialize [AWS Parameter Store] parameters into
//!   Rust structs. *(deserialization only)*
//! - [S-expressions], the textual representation of code and data used by the
//!   Lisp language family.
//! - [D-Bus]'s binary wire format.
//! - [FlexBuffers], the schemaless cousin of Google's FlatBuffers zero-copy serialization format.
//! - [DynamoDB Items], the format used by [rusoto_dynamodb] to transfer data to
//!   and from DynamoDB.
//!
//! [JSON]: https://github.com/serde-rs/json
//! [Bincode]: https://github.com/bincode-org/bincode
//! [CBOR]: https://github.com/enarx/ciborium
//! [YAML]: https://github.com/dtolnay/serde-yaml
//! [MessagePack]: https://github.com/3Hren/msgpack-rust
//! [TOML]: https://github.com/alexcrichton/toml-rs
//! [Pickle]: https://github.com/birkenfeld/serde-pickle
//! [RON]: https://github.com/ron-rs/ron
//! [BSON]: https://github.com/mongodb/bson-rust
//! [Avro]: https://github.com/flavray/avro-rs
//! [JSON5]: https://github.com/callum-oakley/json5-rs
//! [Postcard]: https://github.com/jamesmunns/postcard
//! [URL]: https://docs.rs/serde_qs
//! [Envy]: https://github.com/softprops/envy
//! [Envy Store]: https://github.com/softprops/envy-store
//! [Cargo]: https://doc.rust-lang.org/cargo/reference/manifest.html
//! [AWS Parameter Store]: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html
//! [S-expressions]: https://github.com/rotty/lexpr-rs
//! [D-Bus]: https://docs.rs/zvariant
//! [FlexBuffers]: https://github.com/google/flatbuffers/tree/master/rust/flexbuffers
//! [DynamoDB Items]: https://docs.rs/serde_dynamo
//! [rusoto_dynamodb]: https://docs.rs/rusoto_dynamodb

////////////////////////////////////////////////////////////////////////////////

// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.139")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Unstable functionality only if the user asks for it. For tracking and
// discussion of these features please refer to this issue:
//
//    https://github.com/serde-rs/serde/issues/812
#![cfg_attr(feature = "unstable", feature(never_type))]
#![allow(unknown_lints, bare_trait_objects, deprecated)]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
// Ignored clippy and clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
        unnested_or_patterns,
        // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7768
        semicolon_if_nothing_returned,
        // not available in our oldest supported compiler
        empty_enum,
        type_repetition_in_bounds, // https://github.com/rust-lang/rust-clippy/issues/8772
        // integer and float ser/de requires these sorts of casts
        cast_possible_truncation,
        cast_possible_wrap,
        cast_sign_loss,
        // things are often more readable this way
        cast_lossless,
        module_name_repetitions,
        option_if_let_else,
        single_match_else,
        type_complexity,
        use_self,
        zero_prefixed_literal,
        // correctly used
        derive_partial_eq_without_eq,
        enum_glob_use,
        explicit_auto_deref,
        let_underscore_drop,
        map_err_ignore,
        result_unit_err,
        wildcard_imports,
        // not practical
        needless_pass_by_value,
        similar_names,
        too_many_lines,
        // preference
        doc_markdown,
        unseparated_literal_suffix,
        // false positive
        needless_doctest_main,
        // noisy
        missing_errors_doc,
        must_use_candidate,
    )
)]
// Rustc lints.
#![deny(missing_docs, unused_imports)]

////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "alloc")]
extern crate alloc;

/// A facade around all the types we need from the `std`, `core`, and `alloc`
/// crates. This avoids elaborate import wrangling having to happen in every
/// module.
mod lib {
    mod core {
        #[cfg(not(feature = "std"))]
        pub use core::*;
        #[cfg(feature = "std")]
        pub use std::*;
    }

    pub use self::core::{cmp, iter, mem, num, ptr, slice, str};
    pub use self::core::{f32, f64};
    pub use self::core::{i16, i32, i64, i8, isize};
    pub use self::core::{u16, u32, u64, u8, usize};

    pub use self::core::cell::{Cell, RefCell};
    pub use self::core::clone::{self, Clone};
    pub use self::core::convert::{self, From, Into};
    pub use self::core::default::{self, Default};
    pub use self::core::fmt::{self, Debug, Display};
    pub use self::core::marker::{self, PhantomData};
    pub use self::core::num::Wrapping;
    pub use self::core::ops::Range;
    pub use self::core::option::{self, Option};
    pub use self::core::result::{self, Result};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::borrow::{Cow, ToOwned};
    #[cfg(feature = "std")]
    pub use std::borrow::{Cow, ToOwned};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::string::{String, ToString};
    #[cfg(feature = "std")]
    pub use std::string::{String, ToString};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::vec::Vec;
    #[cfg(feature = "std")]
    pub use std::vec::Vec;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::boxed::Box;
    #[cfg(feature = "std")]
    pub use std::boxed::Box;

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::rc::{Rc, Weak as RcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::rc::{Rc, Weak as RcWeak};

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::sync::{Arc, Weak as ArcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::sync::{Arc, Weak as ArcWeak};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
    #[cfg(feature = "std")]
    pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};

    #[cfg(feature = "std")]
    pub use std::{error, net};

    #[cfg(feature = "std")]
    pub use std::collections::{HashMap, HashSet};
    #[cfg(feature = "std")]
    pub use std::ffi::{CStr, CString, OsStr, OsString};
    #[cfg(feature = "std")]
    pub use std::hash::{BuildHasher, Hash};
    #[cfg(feature = "std")]
    pub use std::io::Write;
    #[cfg(feature = "std")]
    pub use std::path::{Path, PathBuf};
    #[cfg(feature = "std")]
    pub use std::sync::{Mutex, RwLock};
    #[cfg(feature = "std")]
    pub use std::time::{SystemTime, UNIX_EPOCH};

    #[cfg(all(feature = "std", not(no_collections_bound), no_ops_bound))]
    pub use std::collections::Bound;

    #[cfg(not(no_core_reverse))]
    pub use self::core::cmp::Reverse;

    #[cfg(not(no_ops_bound))]
    pub use self::core::ops::Bound;

    #[cfg(not(no_range_inclusive))]
    pub use self::core::ops::RangeInclusive;

    #[cfg(all(feature = "std", not(no_std_atomic)))]
    pub use std::sync::atomic::{
        AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU8,
        AtomicUsize, Ordering,
    };
    #[cfg(all(feature = "std", not(no_std_atomic64)))]
    pub use std::sync::atomic::{AtomicI64, AtomicU64};

    #[cfg(any(feature = "std", not(no_core_duration)))]
    pub use self::core::time::Duration;
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
mod macros;

#[macro_use]
mod integer128;

pub mod de;
pub mod ser;

#[doc(inline)]
pub use de::{Deserialize, Deserializer};
#[doc(inline)]
pub use ser::{Serialize, Serializer};

// Used by generated code and doc tests. Not public API.
#[doc(hidden)]
#[path = "private/mod.rs"]
pub mod __private;

#[allow(unused_imports)]
use self::__private as export;
#[allow(unused_imports)]
use self::__private as private;

#[path = "de/seed.rs"]
mod seed;

#[cfg(not(feature = "std"))]
mod std_error;

// Re-export #[derive(Serialize, Deserialize)].
//
// The reason re-exporting is not enabled by default is that disabling it would
// be annoying for crates that provide handwritten impls or data formats. They
// would need to disable default features and then explicitly re-enable std.
#[cfg(feature = "serde_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[doc(hidden)]
pub use serde_derive::*;

#[cfg(all(not(no_serde_derive), any(feature = "std", feature = "alloc")))]
mod actually_private {
    pub struct T;
}
<commit_msg>Ignore new_without_default clippy lint<commit_after>//! # Serde
//!
//! Serde is a framework for ***ser***ializing and ***de***serializing Rust data
//! structures efficiently and generically.
//!
//! The Serde ecosystem consists of data structures that know how to serialize
//! and deserialize themselves along with data formats that know how to
//! serialize and deserialize other things. Serde provides the layer by which
//! these two groups interact with each other, allowing any supported data
//! structure to be serialized and deserialized using any supported data format.
//!
//! See the Serde website <https://serde.rs/> for additional documentation and
//! usage examples.
//!
//! ## Design
//!
//! Where many other languages rely on runtime reflection for serializing data,
//! Serde is instead built on Rust's powerful trait system. A data structure
//! that knows how to serialize and deserialize itself is one that implements
//! Serde's `Serialize` and `Deserialize` traits (or uses Serde's derive
//! attribute to automatically generate implementations at compile time). This
//! avoids any overhead of reflection or runtime type information. In fact in
//! many situations the interaction between data structure and data format can
//! be completely optimized away by the Rust compiler, leaving Serde
//! serialization to perform the same speed as a handwritten serializer for the
//! specific selection of data structure and data format.
//!
//! ## Data formats
//!
//! The following is a partial list of data formats that have been implemented
//! for Serde by the community.
//!
//! - [JSON], the ubiquitous JavaScript Object Notation used by many HTTP APIs.
//! - [Bincode], a compact binary format
//!   used for IPC within the Servo rendering engine.
//! - [CBOR], a Concise Binary Object Representation designed for small message
//!   size without the need for version negotiation.
//! - [YAML], a self-proclaimed human-friendly configuration language that ain't
//!   markup language.
//! - [MessagePack], an efficient binary format that resembles a compact JSON.
//! - [TOML], a minimal configuration format used by [Cargo].
//! - [Pickle], a format common in the Python world.
//! - [RON], a Rusty Object Notation.
//! - [BSON], the data storage and network transfer format used by MongoDB.
//! - [Avro], a binary format used within Apache Hadoop, with support for schema
//!   definition.
//! - [JSON5], a superset of JSON including some productions from ES5.
//! - [Postcard], a no\_std and embedded-systems friendly compact binary format.
//! - [URL] query strings, in the x-www-form-urlencoded format.
//! - [Envy], a way to deserialize environment variables into Rust structs.
//!   *(deserialization only)*
//! - [Envy Store], a way to deserialize [AWS Parameter Store] parameters into
//!   Rust structs. *(deserialization only)*
//! - [S-expressions], the textual representation of code and data used by the
//!   Lisp language family.
//! - [D-Bus]'s binary wire format.
//! - [FlexBuffers], the schemaless cousin of Google's FlatBuffers zero-copy serialization format.
//! - [DynamoDB Items], the format used by [rusoto_dynamodb] to transfer data to
//!   and from DynamoDB.
//!
//! [JSON]: https://github.com/serde-rs/json
//! [Bincode]: https://github.com/bincode-org/bincode
//! [CBOR]: https://github.com/enarx/ciborium
//! [YAML]: https://github.com/dtolnay/serde-yaml
//! [MessagePack]: https://github.com/3Hren/msgpack-rust
//! [TOML]: https://github.com/alexcrichton/toml-rs
//! [Pickle]: https://github.com/birkenfeld/serde-pickle
//! [RON]: https://github.com/ron-rs/ron
//! [BSON]: https://github.com/mongodb/bson-rust
//! [Avro]: https://github.com/flavray/avro-rs
//! [JSON5]: https://github.com/callum-oakley/json5-rs
//! [Postcard]: https://github.com/jamesmunns/postcard
//! [URL]: https://docs.rs/serde_qs
//! [Envy]: https://github.com/softprops/envy
//! [Envy Store]: https://github.com/softprops/envy-store
//! [Cargo]: https://doc.rust-lang.org/cargo/reference/manifest.html
//! [AWS Parameter Store]: https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html
//! [S-expressions]: https://github.com/rotty/lexpr-rs
//! [D-Bus]: https://docs.rs/zvariant
//! [FlexBuffers]: https://github.com/google/flatbuffers/tree/master/rust/flexbuffers
//! [DynamoDB Items]: https://docs.rs/serde_dynamo
//! [rusoto_dynamodb]: https://docs.rs/rusoto_dynamodb

////////////////////////////////////////////////////////////////////////////////

// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.139")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Unstable functionality only if the user asks for it. For tracking and
// discussion of these features please refer to this issue:
//
//    https://github.com/serde-rs/serde/issues/812
#![cfg_attr(feature = "unstable", feature(never_type))]
#![allow(unknown_lints, bare_trait_objects, deprecated)]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
// Ignored clippy and clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
        unnested_or_patterns,
        // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7768
        semicolon_if_nothing_returned,
        // not available in our oldest supported compiler
        empty_enum,
        type_repetition_in_bounds, // https://github.com/rust-lang/rust-clippy/issues/8772
        // integer and float ser/de requires these sorts of casts
        cast_possible_truncation,
        cast_possible_wrap,
        cast_sign_loss,
        // things are often more readable this way
        cast_lossless,
        module_name_repetitions,
        option_if_let_else,
        single_match_else,
        type_complexity,
        use_self,
        zero_prefixed_literal,
        // correctly used
        derive_partial_eq_without_eq,
        enum_glob_use,
        explicit_auto_deref,
        let_underscore_drop,
        map_err_ignore,
        new_without_default,
        result_unit_err,
        wildcard_imports,
        // not practical
        needless_pass_by_value,
        similar_names,
        too_many_lines,
        // preference
        doc_markdown,
        unseparated_literal_suffix,
        // false positive
        needless_doctest_main,
        // noisy
        missing_errors_doc,
        must_use_candidate,
    )
)]
// Rustc lints.
#![deny(missing_docs, unused_imports)]

////////////////////////////////////////////////////////////////////////////////

#[cfg(feature = "alloc")]
extern crate alloc;

/// A facade around all the types we need from the `std`, `core`, and `alloc`
/// crates. This avoids elaborate import wrangling having to happen in every
/// module.
mod lib {
    mod core {
        #[cfg(not(feature = "std"))]
        pub use core::*;
        #[cfg(feature = "std")]
        pub use std::*;
    }

    pub use self::core::{cmp, iter, mem, num, ptr, slice, str};
    pub use self::core::{f32, f64};
    pub use self::core::{i16, i32, i64, i8, isize};
    pub use self::core::{u16, u32, u64, u8, usize};

    pub use self::core::cell::{Cell, RefCell};
    pub use self::core::clone::{self, Clone};
    pub use self::core::convert::{self, From, Into};
    pub use self::core::default::{self, Default};
    pub use self::core::fmt::{self, Debug, Display};
    pub use self::core::marker::{self, PhantomData};
    pub use self::core::num::Wrapping;
    pub use self::core::ops::Range;
    pub use self::core::option::{self, Option};
    pub use self::core::result::{self, Result};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::borrow::{Cow, ToOwned};
    #[cfg(feature = "std")]
    pub use std::borrow::{Cow, ToOwned};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::string::{String, ToString};
    #[cfg(feature = "std")]
    pub use std::string::{String, ToString};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::vec::Vec;
    #[cfg(feature = "std")]
    pub use std::vec::Vec;

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::boxed::Box;
    #[cfg(feature = "std")]
    pub use std::boxed::Box;

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::rc::{Rc, Weak as RcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::rc::{Rc, Weak as RcWeak};

    #[cfg(all(feature = "rc", feature = "alloc", not(feature = "std")))]
    pub use alloc::sync::{Arc, Weak as ArcWeak};
    #[cfg(all(feature = "rc", feature = "std"))]
    pub use std::sync::{Arc, Weak as ArcWeak};

    #[cfg(all(feature = "alloc", not(feature = "std")))]
    pub use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};
    #[cfg(feature = "std")]
    pub use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque};

    #[cfg(feature = "std")]
    pub use std::{error, net};

    #[cfg(feature = "std")]
    pub use std::collections::{HashMap, HashSet};
    #[cfg(feature = "std")]
    pub use std::ffi::{CStr, CString, OsStr, OsString};
    #[cfg(feature = "std")]
    pub use std::hash::{BuildHasher, Hash};
    #[cfg(feature = "std")]
    pub use std::io::Write;
    #[cfg(feature = "std")]
    pub use std::path::{Path, PathBuf};
    #[cfg(feature = "std")]
    pub use std::sync::{Mutex, RwLock};
    #[cfg(feature = "std")]
    pub use std::time::{SystemTime, UNIX_EPOCH};

    #[cfg(all(feature = "std", not(no_collections_bound), no_ops_bound))]
    pub use std::collections::Bound;

    #[cfg(not(no_core_reverse))]
    pub use self::core::cmp::Reverse;

    #[cfg(not(no_ops_bound))]
    pub use self::core::ops::Bound;

    #[cfg(not(no_range_inclusive))]
    pub use self::core::ops::RangeInclusive;

    #[cfg(all(feature = "std", not(no_std_atomic)))]
    pub use std::sync::atomic::{
        AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, AtomicU8,
        AtomicUsize, Ordering,
    };
    #[cfg(all(feature = "std", not(no_std_atomic64)))]
    pub use std::sync::atomic::{AtomicI64, AtomicU64};

    #[cfg(any(feature = "std", not(no_core_duration)))]
    pub use self::core::time::Duration;
}

////////////////////////////////////////////////////////////////////////////////

#[macro_use]
mod macros;

#[macro_use]
mod integer128;

pub mod de;
pub mod ser;

#[doc(inline)]
pub use de::{Deserialize, Deserializer};
#[doc(inline)]
pub use ser::{Serialize, Serializer};

// Used by generated code and doc tests. Not public API.
#[doc(hidden)]
#[path = "private/mod.rs"]
pub mod __private;

#[allow(unused_imports)]
use self::__private as export;
#[allow(unused_imports)]
use self::__private as private;

#[path = "de/seed.rs"]
mod seed;

#[cfg(not(feature = "std"))]
mod std_error;

// Re-export #[derive(Serialize, Deserialize)].
//
// The reason re-exporting is not enabled by default is that disabling it would
// be annoying for crates that provide handwritten impls or data formats. They
// would need to disable default features and then explicitly re-enable std.
#[cfg(feature = "serde_derive")]
#[allow(unused_imports)]
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde_derive")]
#[doc(hidden)]
pub use serde_derive::*;

#[cfg(all(not(no_serde_derive), any(feature = "std", feature = "alloc")))]
mod actually_private {
    pub struct T;
}
<|endoftext|>


<commit_before>//! Implementation of incremental re-parsing.
//!
//! We use two simple strategies for this:
//!   - if the edit modifies only a single token (like changing an identifier's
//!     letter), we replace only this token.
//!   - otherwise, we search for the nearest `{}` block which contains the edit
//!     and try to parse only this block.

use ra_parser::Reparser;
use ra_text_edit::AtomTextEdit;

use crate::{
    algo,
    parsing::{
        lexer::{lex_single_syntax_kind, tokenize, Token},
        text_token_source::TextTokenSource,
        text_tree_sink::TextTreeSink,
    },
    syntax_node::{GreenNode, GreenToken, NodeOrToken, SyntaxElement, SyntaxNode},
    SyntaxError,
    SyntaxKind::*,
    TextRange, TextUnit, T,
};

pub(crate) fn incremental_reparse(
    node: &SyntaxNode,
    edit: &AtomTextEdit,
    errors: Vec<SyntaxError>,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
    if let Some((green, old_range)) = reparse_token(node, &edit) {
        return Some((green, merge_errors(errors, Vec::new(), old_range, edit), old_range));
    }

    if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) {
        return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
    }
    None
}

fn reparse_token<'node>(
    root: &'node SyntaxNode,
    edit: &AtomTextEdit,
) -> Option<(GreenNode, TextRange)> {
    let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone();
    let prev_token_kind = prev_token.kind();
    match prev_token_kind {
        WHITESPACE | COMMENT | IDENT | STRING | RAW_STRING => {
            if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT {
                // removing a new line may extends previous token
                let deleted_range = edit.delete - prev_token.text_range().start();
                if prev_token.text()[deleted_range].contains('\n') {
                    return None;
                }
            }

            let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit);
            let (new_token_kind, _error) = lex_single_syntax_kind(&new_text)?;

            if new_token_kind != prev_token_kind
                || (new_token_kind == IDENT && is_contextual_kw(&new_text))
            {
                return None;
            }

            // Check that edited token is not a part of the bigger token.
            // E.g. if for source code `bruh"str"` the user removed `ruh`, then
            // `b` no longer remains an identifier, but becomes a part of byte string literal
            if let Some(next_char) = root.text().char_at(prev_token.text_range().end()) {
                new_text.push(next_char);
                let token_with_next_char = lex_single_syntax_kind(&new_text);
                if let Some((_kind, _error)) = token_with_next_char {
                    return None;
                }
                new_text.pop();
            }

            let new_token =
                GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), new_text.into());
            Some((prev_token.replace_with(new_token), prev_token.text_range()))
        }
        _ => None,
    }
}

fn reparse_block<'node>(
    root: &'node SyntaxNode,
    edit: &AtomTextEdit,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
    let (node, reparser) = find_reparsable_node(root, edit.delete)?;
    let text = get_text_after_edit(node.clone().into(), &edit);

    let (tokens, new_lexer_errors) = tokenize(&text);
    if !is_balanced(&tokens) {
        return None;
    }

    let mut token_source = TextTokenSource::new(&text, &tokens);
    let mut tree_sink = TextTreeSink::new(&text, &tokens);
    reparser.parse(&mut token_source, &mut tree_sink);

    let (green, mut new_parser_errors) = tree_sink.finish();
    new_parser_errors.extend(new_lexer_errors);

    Some((node.replace_with(green), new_parser_errors, node.text_range()))
}

fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String {
    let edit =
        AtomTextEdit::replace(edit.delete - element.text_range().start(), edit.insert.clone());

    let text = match element {
        NodeOrToken::Token(token) => token.text().to_string(),
        NodeOrToken::Node(node) => node.text().to_string(),
    };
    edit.apply(text)
}

fn is_contextual_kw(text: &str) -> bool {
    match text {
        "auto" | "default" | "union" => true,
        _ => false,
    }
}

fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> {
    let node = algo::find_covering_element(node, range);

    let mut ancestors = match node {
        NodeOrToken::Token(it) => it.parent().ancestors(),
        NodeOrToken::Node(it) => it.ancestors(),
    };
    ancestors.find_map(|node| {
        let first_child = node.first_child_or_token().map(|it| it.kind());
        let parent = node.parent().map(|it| it.kind());
        Reparser::for_node(node.kind(), first_child, parent).map(|r| (node, r))
    })
}

fn is_balanced(tokens: &[Token]) -> bool {
    if tokens.is_empty()
        || tokens.first().unwrap().kind != T!['{']
        || tokens.last().unwrap().kind != T!['}']
    {
        return false;
    }
    let mut balance = 0usize;
    for t in &tokens[1..tokens.len() - 1] {
        match t.kind {
            T!['{'] => balance += 1,
            T!['}'] => {
                balance = match balance.checked_sub(1) {
                    Some(b) => b,
                    None => return false,
                }
            }
            _ => (),
        }
    }
    balance == 0
}

fn merge_errors(
    old_errors: Vec<SyntaxError>,
    new_errors: Vec<SyntaxError>,
    old_range: TextRange,
    edit: &AtomTextEdit,
) -> Vec<SyntaxError> {
    let mut res = Vec::new();
    for e in old_errors {
        if e.offset() <= old_range.start() {
            res.push(e)
        } else if e.offset() >= old_range.end() {
            res.push(e.add_offset(TextUnit::of_str(&edit.insert), edit.delete.len()));
        }
    }
    for e in new_errors {
        res.push(e.add_offset(old_range.start(), 0.into()));
    }
    res
}

#[cfg(test)]
mod tests {
    use test_utils::{assert_eq_text, extract_range};

    use super::*;
    use crate::{AstNode, Parse, SourceFile};

    fn do_check(before: &str, replace_with: &str, reparsed_len: u32) {
        let (range, before) = extract_range(before);
        let edit = AtomTextEdit::replace(range, replace_with.to_owned());
        let after = edit.apply(before.clone());

        let fully_reparsed = SourceFile::parse(&after);
        let incrementally_reparsed: Parse<SourceFile> = {
            let f = SourceFile::parse(&before);
            // FIXME: it seems this initialization statement is unnecessary (see edit in outer scope)
            // Investigate whether it should really be removed.
            let edit = AtomTextEdit { delete: range, insert: replace_with.to_string() };
            let (green, new_errors, range) =
                incremental_reparse(f.tree().syntax(), &edit, f.errors.to_vec()).unwrap();
            assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
            Parse::new(green, new_errors)
        };

        assert_eq_text!(
            &format!("{:#?}", fully_reparsed.tree().syntax()),
            &format!("{:#?}", incrementally_reparsed.tree().syntax()),
        );
    }

    #[test] // FIXME: some test here actually test token reparsing
    fn reparse_block_tests() {
        do_check(
            r"
fn foo() {
    let x = foo + <|>bar<|>
}
",
            "baz",
            3,
        );
        do_check(
            r"
fn foo() {
    let x = foo<|> + bar<|>
}
",
            "baz",
            25,
        );
        do_check(
            r"
struct Foo {
    f: foo<|><|>
}
",
            ",\n    g: (),",
            14,
        );
        do_check(
            r"
fn foo {
    let;
    1 + 1;
    <|>92<|>;
}
",
            "62",
            31, // FIXME: reparse only int literal here
        );
        do_check(
            r"
mod foo {
    fn <|><|>
}
",
            "bar",
            11,
        );

        do_check(
            r"
trait Foo {
    type <|>Foo<|>;
}
",
            "Output",
            3,
        );
        do_check(
            r"
impl IntoIterator<Item=i32> for Foo {
    f<|><|>
}
",
            "n next(",
            9,
        );
        do_check(r"use a::b::{foo,<|>,bar<|>};", "baz", 10);
        do_check(
            r"
pub enum A {
    Foo<|><|>
}
",
            "\nBar;\n",
            11,
        );
        do_check(
            r"
foo!{a, b<|><|> d}
",
            ", c[3]",
            8,
        );
        do_check(
            r"
fn foo() {
    vec![<|><|>]
}
",
            "123",
            14,
        );
        do_check(
            r"
extern {
    fn<|>;<|>
}
",
            " exit(code: c_int)",
            11,
        );
    }

    #[test]
    fn reparse_token_tests() {
        do_check(
            r"<|><|>
fn foo() -> i32 { 1 }
",
            "\n\n\n   \n",
            1,
        );
        do_check(
            r"
fn foo() -> <|><|> {}
",
            "  \n",
            2,
        );
        do_check(
            r"
fn <|>foo<|>() -> i32 { 1 }
",
            "bar",
            3,
        );
        do_check(
            r"
fn foo<|><|>foo() {  }
",
            "bar",
            6,
        );
        do_check(
            r"
fn foo /* <|><|> */ () {}
",
            "some comment",
            6,
        );
        do_check(
            r"
fn baz <|><|> () {}
",
            "    \t\t\n\n",
            2,
        );
        do_check(
            r"
fn baz <|><|> () {}
",
            "    \t\t\n\n",
            2,
        );
        do_check(
            r"
/// foo <|><|>omment
mod { }
",
            "c",
            14,
        );
        do_check(
            r#"
fn -> &str { "Hello<|><|>" }
"#,
            ", world",
            7,
        );
        do_check(
            r#"
fn -> &str { // "Hello<|><|>"
"#,
            ", world",
            10,
        );
        do_check(
            r##"
fn -> &str { r#"Hello<|><|>"#
"##,
            ", world",
            10,
        );
        do_check(
            r"
#[derive(<|>Copy<|>)]
enum Foo {

}
",
            "Clone",
            4,
        );
    }
}
<commit_msg>ra_syntax: removed unnecessary init statement from reparsing tests<commit_after>//! Implementation of incremental re-parsing.
//!
//! We use two simple strategies for this:
//!   - if the edit modifies only a single token (like changing an identifier's
//!     letter), we replace only this token.
//!   - otherwise, we search for the nearest `{}` block which contains the edit
//!     and try to parse only this block.

use ra_parser::Reparser;
use ra_text_edit::AtomTextEdit;

use crate::{
    algo,
    parsing::{
        lexer::{lex_single_syntax_kind, tokenize, Token},
        text_token_source::TextTokenSource,
        text_tree_sink::TextTreeSink,
    },
    syntax_node::{GreenNode, GreenToken, NodeOrToken, SyntaxElement, SyntaxNode},
    SyntaxError,
    SyntaxKind::*,
    TextRange, TextUnit, T,
};

pub(crate) fn incremental_reparse(
    node: &SyntaxNode,
    edit: &AtomTextEdit,
    errors: Vec<SyntaxError>,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
    if let Some((green, old_range)) = reparse_token(node, &edit) {
        return Some((green, merge_errors(errors, Vec::new(), old_range, edit), old_range));
    }

    if let Some((green, new_errors, old_range)) = reparse_block(node, &edit) {
        return Some((green, merge_errors(errors, new_errors, old_range, edit), old_range));
    }
    None
}

fn reparse_token<'node>(
    root: &'node SyntaxNode,
    edit: &AtomTextEdit,
) -> Option<(GreenNode, TextRange)> {
    let prev_token = algo::find_covering_element(root, edit.delete).as_token()?.clone();
    let prev_token_kind = prev_token.kind();
    match prev_token_kind {
        WHITESPACE | COMMENT | IDENT | STRING | RAW_STRING => {
            if prev_token_kind == WHITESPACE || prev_token_kind == COMMENT {
                // removing a new line may extends previous token
                let deleted_range = edit.delete - prev_token.text_range().start();
                if prev_token.text()[deleted_range].contains('\n') {
                    return None;
                }
            }

            let mut new_text = get_text_after_edit(prev_token.clone().into(), &edit);
            let (new_token_kind, _error) = lex_single_syntax_kind(&new_text)?;

            if new_token_kind != prev_token_kind
                || (new_token_kind == IDENT && is_contextual_kw(&new_text))
            {
                return None;
            }

            // Check that edited token is not a part of the bigger token.
            // E.g. if for source code `bruh"str"` the user removed `ruh`, then
            // `b` no longer remains an identifier, but becomes a part of byte string literal
            if let Some(next_char) = root.text().char_at(prev_token.text_range().end()) {
                new_text.push(next_char);
                let token_with_next_char = lex_single_syntax_kind(&new_text);
                if let Some((_kind, _error)) = token_with_next_char {
                    return None;
                }
                new_text.pop();
            }

            let new_token =
                GreenToken::new(rowan::SyntaxKind(prev_token_kind.into()), new_text.into());
            Some((prev_token.replace_with(new_token), prev_token.text_range()))
        }
        _ => None,
    }
}

fn reparse_block<'node>(
    root: &'node SyntaxNode,
    edit: &AtomTextEdit,
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
    let (node, reparser) = find_reparsable_node(root, edit.delete)?;
    let text = get_text_after_edit(node.clone().into(), &edit);

    let (tokens, new_lexer_errors) = tokenize(&text);
    if !is_balanced(&tokens) {
        return None;
    }

    let mut token_source = TextTokenSource::new(&text, &tokens);
    let mut tree_sink = TextTreeSink::new(&text, &tokens);
    reparser.parse(&mut token_source, &mut tree_sink);

    let (green, mut new_parser_errors) = tree_sink.finish();
    new_parser_errors.extend(new_lexer_errors);

    Some((node.replace_with(green), new_parser_errors, node.text_range()))
}

fn get_text_after_edit(element: SyntaxElement, edit: &AtomTextEdit) -> String {
    let edit =
        AtomTextEdit::replace(edit.delete - element.text_range().start(), edit.insert.clone());

    let text = match element {
        NodeOrToken::Token(token) => token.text().to_string(),
        NodeOrToken::Node(node) => node.text().to_string(),
    };
    edit.apply(text)
}

fn is_contextual_kw(text: &str) -> bool {
    match text {
        "auto" | "default" | "union" => true,
        _ => false,
    }
}

fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> {
    let node = algo::find_covering_element(node, range);

    let mut ancestors = match node {
        NodeOrToken::Token(it) => it.parent().ancestors(),
        NodeOrToken::Node(it) => it.ancestors(),
    };
    ancestors.find_map(|node| {
        let first_child = node.first_child_or_token().map(|it| it.kind());
        let parent = node.parent().map(|it| it.kind());
        Reparser::for_node(node.kind(), first_child, parent).map(|r| (node, r))
    })
}

fn is_balanced(tokens: &[Token]) -> bool {
    if tokens.is_empty()
        || tokens.first().unwrap().kind != T!['{']
        || tokens.last().unwrap().kind != T!['}']
    {
        return false;
    }
    let mut balance = 0usize;
    for t in &tokens[1..tokens.len() - 1] {
        match t.kind {
            T!['{'] => balance += 1,
            T!['}'] => {
                balance = match balance.checked_sub(1) {
                    Some(b) => b,
                    None => return false,
                }
            }
            _ => (),
        }
    }
    balance == 0
}

fn merge_errors(
    old_errors: Vec<SyntaxError>,
    new_errors: Vec<SyntaxError>,
    old_range: TextRange,
    edit: &AtomTextEdit,
) -> Vec<SyntaxError> {
    let mut res = Vec::new();
    for e in old_errors {
        if e.offset() <= old_range.start() {
            res.push(e)
        } else if e.offset() >= old_range.end() {
            res.push(e.add_offset(TextUnit::of_str(&edit.insert), edit.delete.len()));
        }
    }
    for e in new_errors {
        res.push(e.add_offset(old_range.start(), 0.into()));
    }
    res
}

#[cfg(test)]
mod tests {
    use test_utils::{assert_eq_text, extract_range};

    use super::*;
    use crate::{AstNode, Parse, SourceFile};

    fn do_check(before: &str, replace_with: &str, reparsed_len: u32) {
        let (range, before) = extract_range(before);
        let edit = AtomTextEdit::replace(range, replace_with.to_owned());
        let after = edit.apply(before.clone());

        let fully_reparsed = SourceFile::parse(&after);
        let incrementally_reparsed: Parse<SourceFile> = {
            let f = SourceFile::parse(&before);
            let (green, new_errors, range) =
                incremental_reparse(f.tree().syntax(), &edit, f.errors.to_vec()).unwrap();
            assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
            Parse::new(green, new_errors)
        };

        assert_eq_text!(
            &format!("{:#?}", fully_reparsed.tree().syntax()),
            &format!("{:#?}", incrementally_reparsed.tree().syntax()),
        );
    }

    #[test] // FIXME: some test here actually test token reparsing
    fn reparse_block_tests() {
        do_check(
            r"
fn foo() {
    let x = foo + <|>bar<|>
}
",
            "baz",
            3,
        );
        do_check(
            r"
fn foo() {
    let x = foo<|> + bar<|>
}
",
            "baz",
            25,
        );
        do_check(
            r"
struct Foo {
    f: foo<|><|>
}
",
            ",\n    g: (),",
            14,
        );
        do_check(
            r"
fn foo {
    let;
    1 + 1;
    <|>92<|>;
}
",
            "62",
            31, // FIXME: reparse only int literal here
        );
        do_check(
            r"
mod foo {
    fn <|><|>
}
",
            "bar",
            11,
        );

        do_check(
            r"
trait Foo {
    type <|>Foo<|>;
}
",
            "Output",
            3,
        );
        do_check(
            r"
impl IntoIterator<Item=i32> for Foo {
    f<|><|>
}
",
            "n next(",
            9,
        );
        do_check(r"use a::b::{foo,<|>,bar<|>};", "baz", 10);
        do_check(
            r"
pub enum A {
    Foo<|><|>
}
",
            "\nBar;\n",
            11,
        );
        do_check(
            r"
foo!{a, b<|><|> d}
",
            ", c[3]",
            8,
        );
        do_check(
            r"
fn foo() {
    vec![<|><|>]
}
",
            "123",
            14,
        );
        do_check(
            r"
extern {
    fn<|>;<|>
}
",
            " exit(code: c_int)",
            11,
        );
    }

    #[test]
    fn reparse_token_tests() {
        do_check(
            r"<|><|>
fn foo() -> i32 { 1 }
",
            "\n\n\n   \n",
            1,
        );
        do_check(
            r"
fn foo() -> <|><|> {}
",
            "  \n",
            2,
        );
        do_check(
            r"
fn <|>foo<|>() -> i32 { 1 }
",
            "bar",
            3,
        );
        do_check(
            r"
fn foo<|><|>foo() {  }
",
            "bar",
            6,
        );
        do_check(
            r"
fn foo /* <|><|> */ () {}
",
            "some comment",
            6,
        );
        do_check(
            r"
fn baz <|><|> () {}
",
            "    \t\t\n\n",
            2,
        );
        do_check(
            r"
fn baz <|><|> () {}
",
            "    \t\t\n\n",
            2,
        );
        do_check(
            r"
/// foo <|><|>omment
mod { }
",
            "c",
            14,
        );
        do_check(
            r#"
fn -> &str { "Hello<|><|>" }
"#,
            ", world",
            7,
        );
        do_check(
            r#"
fn -> &str { // "Hello<|><|>"
"#,
            ", world",
            10,
        );
        do_check(
            r##"
fn -> &str { r#"Hello<|><|>"#
"##,
            ", world",
            10,
        );
        do_check(
            r"
#[derive(<|>Copy<|>)]
enum Foo {

}
",
            "Clone",
            4,
        );
    }
}
<|endoftext|>


<commit_before>//! soundstream.rs
//!
//! A real-time soundstream demo that smoothly copies input
//! from a microphone straight to the output. If <Space> is
//! pressed, the SoundStream thread will begin to calculate
//! and print the samplerate to demonstrate the inter-task
//! event handling.
//!
//! Note: Beware of feedback!

#![feature(globs)]

extern crate piston;

use piston::{
    keyboard,
    AssetStore,
    Game,
    GameEvent,
    GameWindow,
    GameWindowSDL2,
    GameWindowSettings,
    KeyPress,
    KeyPressArgs,
    SoundStream,
    SoundStreamSettings
};

// Structs
//------------------------------

/// Main application struct.
pub struct App {
    /// Channel for sending information to the audio stream.
    stream_chan: Option<Sender<GameEvent<'static>>> // Channel for sending Events.
}

/// The audio is non-blocking and needs it's own struct.
pub struct AppSoundStream {
    /// Channel for receiving game events from main game stream.
    chan: Option<Receiver<GameEvent<'static>>>, // Channel for receiving Events.
    should_exit: bool, // Trigger for closing the stream.
    should_print: bool, // Toggle for printing the sample_rate.
    buffer: Vec<f32> // Buffer for passing input to output.
}

// Game Method Implementations
//------------------------------

impl Game for App {

    /// Setup / load the app stuff ready for the main loop.
    /// If using a SoundStream, it must be created within this method.
    fn load(&mut self, asset_store: &mut AssetStore) {

        // Create a channel for communicating events with the soundstream.
        // Note: this channel is used for sending InteractiveEvents, but
        // the same technique could be used here to create custom channels
        // that can safely send any kind of unique data.
        let (send, recv) = channel();
        self.stream_chan = Some(send);

        // Create the soundstream on it's own thread for non-blocking, real-time audio.
        // "soundstreamer" will setup and iterate soundstream using portaudio.
        spawn(proc() {
            let mut soundstream =
                AppSoundStream::new(Some(recv)).run(SoundStreamSettings::cd_quality());
        });

    }

    /// Keypress callback.
    fn key_press(&mut self, args: &KeyPressArgs) {
        println!("Game thread key: {}", args.key);
    }

    /*
    /// Specify the event sending channel. This must be done if we wish
    /// to send interactive events to the SoundStream.
    fn get_event_sender(&self) -> Option<Sender<GameEvent<'static>>> {
        self.stream_chan.clone()
    }
    */
}

impl Drop for App {
    /// Tell the soundstream to exit when App is destroyed.
    fn drop(&mut self) {
        let chan = self.stream_chan.clone();
        match chan {
            Some(sender) => sender.send(KeyPress(KeyPressArgs { key: keyboard::Escape })),
            None => ()
        }
    }
}

impl App {
    /// Creates a new application.
    pub fn new() -> App {
        App {
            stream_chan: None
        }
    }
}

// SoundStream Method Implementations
//------------------------------

impl SoundStream for AppSoundStream {

    /// Load (called prior to main soundstream loop).
    fn load(&mut self) {
        println!("Press <Spacebar> to start/stop printing the real-time sample rate.");
    }

    /// Update (gets called prior to audio_in/audio_out).
    fn update(&mut self, settings: &SoundStreamSettings, dt: u64) {
        if self.should_print {
            let dtsec: f64 = dt as f64 / 1000000000f64;
            println!("Real-time sample rate: {}", (1f64 / dtsec) * settings.frames as f64);
        }
    }

    /// AudioInput
    fn audio_in(&mut self, input: &Vec<f32>, settings: &SoundStreamSettings) {
        self.buffer = input.clone();
    }

    /// AudioOutput
    fn audio_out(&mut self, output: &mut Vec<f32>, settings: &SoundStreamSettings) {
        *output = self.buffer.clone()
    }

    /// KeyPress
    fn key_press(&mut self, args: &KeyPressArgs) {
        println!("Soundstream thread key: {}", args.key);
        if args.key == keyboard::Space {
            let print = if self.should_print { false } else { true };
            self.should_print = print;
        }
        if args.key == keyboard::Escape {
            self.should_exit = true;
        }
    }

    /*
    /// Retrieve Events for callback (i.e. mouse, keyboard).
    fn check_for_event(&self) -> Option<GameEvent<'static>> {
        match self.chan {
            Some(ref receiver) => match receiver.try_recv() {
                Ok(event) => Some(event),
                Err(_) => None
            },
            None => None
        }
    }
    */

    /// Setup the exit condition (is checked once per buffer).
    fn exit(&self) -> bool { self.should_exit }

}

impl AppSoundStream {
    /// AppSoundStream constructor.
    pub fn new(recv: Option<Receiver<GameEvent<'static>>>) -> AppSoundStream {
        AppSoundStream {
            chan: recv,
            should_exit: false,
            should_print: false,
            buffer: vec![]
        }
    }
}

// Main
//------------------------------

#[start]
fn start(argc: int, argv: **u8) -> int {
    // Run gui on the main thread.
    native::start(argc, argv, main)
}

fn main() {
    let mut window: GameWindowSDL2 = GameWindow::new(
        GameWindowSettings {
            title: "soundstream".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            background_color: [0.1, 0.1, 0.1, 0.1],
        }
    );

    let mut asset_store = AssetStore::from_folder("assets");
    let mut app = App::new();
    app.run(&mut window, &mut asset_store);
}

//------------------------------
<commit_msg>Closes #366 and #357<commit_after>//! soundstream.rs
//!
//! A real-time soundstream demo that smoothly copies input
//! from a microphone straight to the output. If <Space> is
//! pressed, the SoundStream thread will begin to calculate
//! and print the samplerate to demonstrate the inter-task
//! event handling.
//!
//! Note: Beware of feedback!

#![feature(globs)]

extern crate piston;

use piston::{
    keyboard,
    AssetStore,
    Game,
    GameWindow,
    GameWindowSDL2,
    GameWindowSettings,
    KeyPressArgs,
    SoundStream,
    SoundStreamSettings
};

// Structs
//------------------------------

/// Main application struct.
pub struct App {
    /// Channel for sending information to the audio stream.
    kill_chan: Option<Sender<bool>> // Channel for sending kill message.
}

/// The audio is non-blocking and needs it's own struct.
pub struct AppSoundStream {
    /// Channel for receiving game events from main game stream.
    kill_chan: Option<Receiver<bool>>, // Channel for receiving kill message.
    should_exit: bool, // Trigger for closing the stream.
    should_print: bool, // Toggle for printing the sample_rate.
    buffer: Vec<f32> // Buffer for passing input to output.
}

// Game Method Implementations
//------------------------------

impl Game for App {

    /// Setup / load the app stuff ready for the main loop.
    /// If using a SoundStream, it must be created within this method.
    fn load(&mut self, asset_store: &mut AssetStore) {

        // Create a channel for communicating events with the soundstream.
        // Note: this channel is used for sending InteractiveEvents, but
        // the same technique could be used here to create custom channels
        // that can safely send any kind of unique data.
        let (send, recv) = channel();
        self.kill_chan = Some(send);

        // Create the soundstream on it's own thread for non-blocking, real-time audio.
        // "soundstreamer" will setup and iterate soundstream using portaudio.
        spawn(proc() {
            let mut soundstream =
                AppSoundStream::new(Some(recv)).run(SoundStreamSettings::cd_quality());
        });

    }

    /// Keypress callback.
    fn key_press(&mut self, args: &KeyPressArgs) {
        println!("Game thread key: {}", args.key);
    }

    /*
    /// Specify the event sending channel. This must be done if we wish
    /// to send interactive events to the SoundStream.
    fn get_event_sender(&self) -> Option<Sender<GameEvent<'static>>> {
        self.stream_chan.clone()
    }
    */
}

impl Drop for App {
    /// Tell the soundstream to exit when App is destroyed.
    fn drop(&mut self) {
        let chan = self.kill_chan.clone();
        match chan {
            Some(sender) => sender.send(true),
            None => ()
        }
    }
}

impl App {
    /// Creates a new application.
    pub fn new() -> App {
        App {
            kill_chan: None
        }
    }
}

// SoundStream Method Implementations
//------------------------------

impl SoundStream for AppSoundStream {

    /// Load (called prior to main soundstream loop).
    fn load(&mut self) {
        println!("Press <Spacebar> to start/stop printing the real-time sample rate.");
    }

    /// Update (gets called prior to audio_in/audio_out).
    fn update(&mut self, settings: &SoundStreamSettings, dt: u64) {
        if self.should_print {
            let dtsec: f64 = dt as f64 / 1000000000f64;
            println!("Real-time sample rate: {}", (1f64 / dtsec) * settings.frames as f64);
        }
        match self.kill_chan {
            Some(ref receiver) => match receiver.try_recv() {
                Ok(_) => self.should_exit = true,
                Err(_) => ()
            },
            None => ()
        }
    }

    /// AudioInput
    fn audio_in(&mut self, input: &Vec<f32>, settings: &SoundStreamSettings) {
        self.buffer = input.clone();
    }

    /// AudioOutput
    fn audio_out(&mut self, output: &mut Vec<f32>, settings: &SoundStreamSettings) {
        *output = self.buffer.clone()
    }

    /// KeyPress
    fn key_press(&mut self, args: &KeyPressArgs) {
        println!("Soundstream thread key: {}", args.key);
        if args.key == keyboard::Space {
            let print = if self.should_print { false } else { true };
            self.should_print = print;
        }
        if args.key == keyboard::Escape {
            self.should_exit = true;
        }
    }

    /*
    /// Retrieve Events for callback (i.e. mouse, keyboard).
    fn check_for_event(&self) -> Option<GameEvent<'static>> {
        match self.chan {
            Some(ref receiver) => match receiver.try_recv() {
                Ok(event) => Some(event),
                Err(_) => None
            },
            None => None
        }
    }
    */

    /// Setup the exit condition (is checked once per buffer).
    fn exit(&self) -> bool { self.should_exit }

}

impl AppSoundStream {
    /// AppSoundStream constructor.
    pub fn new(recv: Option<Receiver<bool>>) -> AppSoundStream {
        AppSoundStream {
            kill_chan: recv,
            should_exit: false,
            should_print: false,
            buffer: vec![]
        }
    }
}

// Main
//------------------------------

#[start]
fn start(argc: int, argv: **u8) -> int {
    // Run gui on the main thread.
    native::start(argc, argv, main)
}

fn main() {
    let mut window: GameWindowSDL2 = GameWindow::new(
        GameWindowSettings {
            title: "soundstream".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
            background_color: [0.1, 0.1, 0.1, 0.1],
        }
    );

    let mut asset_store = AssetStore::from_folder("assets");
    let mut app = App::new();
    app.run(&mut window, &mut asset_store);
}

//------------------------------
<|endoftext|>


<commit_before>use ::inflector::Inflector;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::method;
use infer::TokensResult;

fn mod_ident(path: &str) -> Ident {
    Ident::new(path.replace('/', " ").trim().to_snake_case())
}

pub(super) fn infer_v3(path: &str, schema: &Yaml) -> TokensResult {
    let path_mod = mod_ident(path);

    let method_schemata = schema.as_hash().expect("Path must be a map.");
    let mut method_impls = quote!{};

    for (method, method_schema) in method_schemata {
        let method = method.as_str().expect("Method must be a string.");
        method_impls.append(method::infer_v3(&method, &method_schema)?);
    }

    Ok(quote! {
        pub(super) mod #path_mod {
            use super::tapioca;

            use super::schema_ref;
            use super::API_URL;

            const API_PATH: &'static str = #path;

            #method_impls
        }
    })
}
<commit_msg>Fix importing of main lib<commit_after>use ::inflector::Inflector;
use ::syn::Ident;
use ::yaml_rust::Yaml;

use infer::method;
use infer::TokensResult;

fn mod_ident(path: &str) -> Ident {
    Ident::new(path.replace('/', " ").trim().to_snake_case())
}

pub(super) fn infer_v3(path: &str, schema: &Yaml) -> TokensResult {
    let path_mod = mod_ident(path);

    let method_schemata = schema.as_hash().expect("Path must be a map.");
    let mut method_impls = quote!{};

    for (method, method_schema) in method_schemata {
        let method = method.as_str().expect("Method must be a string.");
        method_impls.append(method::infer_v3(&method, &method_schema)?);
    }

    Ok(quote! {
        pub(super) mod #path_mod {
            use ::tapioca;

            use super::schema_ref;
            use super::API_URL;

            const API_PATH: &'static str = #path;

            #method_impls
        }
    })
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate scraper;
extern crate chrono;
extern crate dotenv;

use hyper::Client;

use scraper::{Html, Selector};

use chrono::{UTC, TimeZone};

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::env;

const NEWS_URL: &'static str = "http://na.finalfantasyxiv.com/lodestone/news/";

struct NewsScraper {
  client: Client,
  database: NewsDatabase
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct NewsDatabase {
  items: HashMap<String, NewsItem>
}

#[derive(Debug, Serialize, Deserialize)]
struct NewsItem {
  title: String,
  url: String,
  kind: NewsKind,
  time: i64,
  tag: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
enum NewsKind {
  SpecialNotice,
  News,
  Topic
}

impl ToString for NewsKind {
  fn to_string(&self) -> String {
    match *self {
      NewsKind::News => "News",
      NewsKind::Topic => "Topic",
      NewsKind::SpecialNotice => "Special notice"
    }.to_string()
  }
}

fn main() {
  dotenv::dotenv().ok();

  let webhook_url = match env::var("LB_NEWS_WEBHOOK") {
    Ok(u) => u,
    Err(_) => {
      println!("No webhook specified");
      return;
    }
  };

  let database = match NewsDatabase::load() {
    Some(db) => db,
    None => NewsDatabase::default()
  };
  let mut scraper = NewsScraper::new(database);

  let news = match scraper.download_news() {
    Some(n) => n,
    None => {
      println!("could not download news");
      return;
    }
  };

  let downloaded_news = scraper.parse_news(&news);
  for (id, item) in downloaded_news {
    if !scraper.database.items.contains_key(&id) {
      let mut embed = json!({
        "type": "rich",
        "timestamp": UTC.timestamp(item.time, 0).to_rfc3339(),
        // "url": item.url,
        "fields": [
          {
            "name": "Title",
            "value": item.title,
            "inline": false
          },
          {
            "name": "Link",
            "value": item.url,
            "inline": false
          },
          {
            "name": "Kind",
            "value": item.kind.to_string(),
            "inline": true
          }
        ]
      });
      if let Some(ref tag) = item.tag {
        embed["fields"].as_array_mut().unwrap().push(json!({
          "name": "Tag",
          "value": tag,
          "inline": true
        }));
      }
      let data = json!({
        "embeds": [embed]
      });
      scraper.database.items.insert(id, item);
      let res = scraper.client.post(&webhook_url)
        .header(hyper::header::ContentType::json())
        .body(&data.to_string())
        .send();
      let mut data = match res {
        Ok(r) => r,
        Err(e) => {
          println!("error sending webhook: {}", e);
          continue;
        }
      };
      let mut content = String::new();
      if let Err(e) = data.read_to_string(&mut content) {
        println!("could not read webhook response: {}", e);
        continue;
      }
      if data.status.class() != hyper::status::StatusClass::Success {
        println!("discord says no");
        println!("{}", content);
      } else {
        println!("webhook sent");
      }
    }
  }
  if scraper.database.save().is_none() {
    println!("could not save database");
  }
}

impl NewsDatabase {
  fn load() -> Option<Self> {
    let news_file = match File::open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::from_reader(news_file).ok()
  }

  fn save(&self) -> Option<()> {
    let news_file = match OpenOptions::new().create(true).write(true).open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::to_writer(news_file, self).ok()
  }
}

impl NewsScraper {
  fn new(database: NewsDatabase) -> Self {
    NewsScraper {
      client: Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
      database: database
    }
  }

  fn download_news(&self) -> Option<String> {
    let mut response = match self.client.get(NEWS_URL).send() {
      Ok(r) => r,
      Err(_) => return None
    };

    let mut content = String::new();
    if response.read_to_string(&mut content).is_err() {
      return None;
    }
    Some(content)
  }

  fn parse_news(&self, news: &str) -> HashMap<String, NewsItem> {
    let html = Html::parse_document(news);
    let li_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(2) > li").unwrap();
    let title_selector = Selector::parse("p.news__list--title").unwrap();
    let time_script_selector = Selector::parse("time.news__list--time > script").unwrap();
    let lis: Vec<_> = html.select(&li_selector).collect();
    let mut items = HashMap::with_capacity(lis.len());
    for li in lis {
      let child = match li.first_child().and_then(|v| v.value().as_element()) {
        Some(c) => c,
        None => {
          println!("invalid news item");
          continue;
        }
      };

      let href = match child.attr("href") {
        Some(h) => h,
        None => {
          println!("invalid link in news item");
          continue;
        }
      };

      let title = match li.select(&title_selector).next() {
        Some(t) => t,
        None => {
          println!("missing title in news item");
          continue;
        }
      };

      let tag: Option<String> = title.first_child()
        .and_then(scraper::ElementRef::wrap)
        .map(|c| c.text().collect::<String>())
        .map(|tag| tag[1..tag.len() - 1].to_string());

      let text_iter = title.text();
      let title: String = if tag.is_some() {
        text_iter.skip(1).collect()
      } else {
        text_iter.collect()
      };

      let time_script = match li.select(&time_script_selector).next() {
        Some(ts) => ts,
        None => {
          println!("news item missing time script");
          continue
        }
      };

      let time_script: String = time_script.text().collect();
      let time_string = match time_script.split("strftime(").nth(1).and_then(|v| v.split(',').next()) {
        Some(time) => time,
        None => {
          println!("invalid script in news item");
          continue
        }
      };
      let time: i64 = match time_string.parse() {
        Ok(t) => t,
        Err(_) => {
          println!("invalid time in time script");
          continue
        }
      };

      let id = match href.split('/').last() {
        Some(i) => i,
        None => {
          println!("invalid href in news item");
          continue;
        }
      };

      let news_item = NewsItem {
        title: title,
        url: format!("http://na.finalfantasyxiv.com{}", href),
        kind: NewsKind::News,
        time: time,
        tag: tag
      };
      items.insert(id.to_string(), news_item);
    }

    items
  }
}
<commit_msg>feat: parse topics as well<commit_after>extern crate hyper;
extern crate hyper_rustls;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate scraper;
extern crate chrono;
extern crate dotenv;

use hyper::Client;

use scraper::{Html, Selector};

use chrono::{UTC, TimeZone};

use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::Read;
use std::env;

const NEWS_URL: &'static str = "http://na.finalfantasyxiv.com/lodestone/news/";

struct NewsScraper {
  client: Client,
  database: NewsDatabase
}

#[derive(Debug, Default, Serialize, Deserialize)]
struct NewsDatabase {
  items: HashMap<String, NewsItem>
}

#[derive(Debug, Serialize, Deserialize)]
struct NewsItem {
  title: String,
  url: String,
  kind: NewsKind,
  time: i64,
  tag: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
enum NewsKind {
  SpecialNotice,
  News,
  Topic
}

impl ToString for NewsKind {
  fn to_string(&self) -> String {
    match *self {
      NewsKind::News => "News",
      NewsKind::Topic => "Topic",
      NewsKind::SpecialNotice => "Special notice"
    }.to_string()
  }
}

fn main() {
  dotenv::dotenv().ok();

  let webhook_url = match env::var("LB_NEWS_WEBHOOK") {
    Ok(u) => u,
    Err(_) => {
      println!("No webhook specified");
      return;
    }
  };

  let database = match NewsDatabase::load() {
    Some(db) => db,
    None => NewsDatabase::default()
  };
  let mut scraper = NewsScraper::new(database);

  let news = match scraper.download_news() {
    Some(n) => n,
    None => {
      println!("could not download news");
      return;
    }
  };

  let downloaded_news = scraper.parse_news(&news);
  for (id, item) in downloaded_news {
    if !scraper.database.items.contains_key(&id) {
      let mut embed = json!({
        "type": "rich",
        "timestamp": UTC.timestamp(item.time, 0).to_rfc3339(),
        // "url": item.url,
        "fields": [
          {
            "name": "Title",
            "value": item.title,
            "inline": false
          },
          {
            "name": "Link",
            "value": item.url,
            "inline": false
          },
          {
            "name": "Kind",
            "value": item.kind.to_string(),
            "inline": true
          }
        ]
      });
      if let Some(ref tag) = item.tag {
        embed["fields"].as_array_mut().unwrap().push(json!({
          "name": "Tag",
          "value": tag,
          "inline": true
        }));
      }
      let data = json!({
        "embeds": [embed]
      });
      println!("{:#?}", item);
      // scraper.database.items.insert(id, item);
      // let res = scraper.client.post(&webhook_url)
      //   .header(hyper::header::ContentType::json())
      //   .body(&data.to_string())
      //   .send();
      // let mut data = match res {
      //   Ok(r) => r,
      //   Err(e) => {
      //     println!("error sending webhook: {}", e);
      //     continue;
      //   }
      // };
      // let mut content = String::new();
      // if let Err(e) = data.read_to_string(&mut content) {
      //   println!("could not read webhook response: {}", e);
      //   continue;
      // }
      // if data.status.class() != hyper::status::StatusClass::Success {
      //   println!("discord says no");
      //   println!("{}", content);
      // } else {
      //   println!("webhook sent");
      // }
    }
  }
  if scraper.database.save().is_none() {
    println!("could not save database");
  }
}

impl NewsDatabase {
  fn load() -> Option<Self> {
    let news_file = match File::open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::from_reader(news_file).ok()
  }

  fn save(&self) -> Option<()> {
    let news_file = match OpenOptions::new().create(true).write(true).open("./lodestone_news.json") {
      Ok(n) => n,
      Err(_) => return None
    };
    serde_json::to_writer(news_file, self).ok()
  }
}

impl NewsScraper {
  fn new(database: NewsDatabase) -> Self {
    NewsScraper {
      client: Client::with_connector(hyper::net::HttpsConnector::new(hyper_rustls::TlsClient::new())),
      database: database
    }
  }

  fn download_news(&self) -> Option<String> {
    let mut response = match self.client.get(NEWS_URL).send() {
      Ok(r) => r,
      Err(_) => return None
    };

    let mut content = String::new();
    if response.read_to_string(&mut content).is_err() {
      return None;
    }
    Some(content)
  }

  fn parse_news(&self, news: &str) -> HashMap<String, NewsItem> {
    let html = Html::parse_document(news);
    let news_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(2) > li").unwrap();
    let topics_selector = Selector::parse("div.news__content.parts__space--add > ul:nth-of-type(3) > li").unwrap();
    let title_selector = Selector::parse("p.news__list--title").unwrap();
    let time_script_selector = Selector::parse("time.news__list--time > script").unwrap();
    let mut lis: Vec<_> = html.select(&news_selector).map(|x| (NewsKind::News, x)).collect();
    lis.append(&mut html.select(&topics_selector).map(|x| (NewsKind::Topic, x)).collect());
    let mut items = HashMap::with_capacity(lis.len());
    for (kind, li) in lis {
      let child = match kind {
        NewsKind::News => li.first_child().and_then(|v| v.value().as_element()),
        NewsKind::Topic => li.select(&title_selector).next().and_then(|v| v.first_child().and_then(|x| x.value().as_element())),
        _ => {
          println!("unsupported news kind");
          continue;
        }
      };

      let child = match child {
        Some(c) => c,
        None => {
          println!("could not get news item child");
          continue
        }
      };

      let href = match child.attr("href") {
        Some(h) => h,
        None => {
          println!("invalid link in news item");
          continue;
        }
      };

      let (title, tag) = match kind {
        NewsKind::News => {
          let title = match li.select(&title_selector).next() {
            Some(t) => t,
            None => {
              println!("missing title in news item");
              continue;
            }
          };

          let tag: Option<String> = title.first_child()
            .and_then(scraper::ElementRef::wrap)
            .map(|c| c.text().collect::<String>())
            .map(|tag| tag[1..tag.len() - 1].to_string());

          let text_iter = title.text();
          let title: String = if tag.is_some() {
            text_iter.skip(1).collect()
          } else {
            text_iter.collect()
          };

          (title, tag)
        },
        NewsKind::Topic => {
          let text = li.select(&title_selector).next()
            .and_then(|v| v.first_child())
            .and_then(scraper::ElementRef::wrap)
            .map(|v| v.text().collect());
          match text {
            Some(t) => (t, None),
            None => {
              println!("invalid topic: no title");
              continue;
            }
          }
        },
        _ => unreachable!()
      };

      let time_script = match li.select(&time_script_selector).next() {
        Some(ts) => ts,
        None => {
          println!("news item missing time script");
          continue
        }
      };

      let time_script: String = time_script.text().collect();
      let time_string = match time_script.split("strftime(").nth(1).and_then(|v| v.split(',').next()) {
        Some(time) => time,
        None => {
          println!("invalid script in news item");
          continue
        }
      };
      let time: i64 = match time_string.parse() {
        Ok(t) => t,
        Err(_) => {
          println!("invalid time in time script");
          continue
        }
      };

      let id = match href.split('/').last() {
        Some(i) => i,
        None => {
          println!("invalid href in news item");
          continue;
        }
      };

      let news_item = NewsItem {
        title: title,
        url: format!("http://na.finalfantasyxiv.com{}", href),
        kind: kind,
        time: time,
        tag: tag
      };
      items.insert(id.to_string(), news_item);
    }

    items
  }
}
<|endoftext|>


<commit_before>
extern crate base64;
extern crate clap;
extern crate openssl;

use openssl::symm::{Cipher, Crypter, Mode};
use std::io::{self, Read};
use clap::{App, Arg};

fn new_crypter_unpadded(
        cipher: Cipher,
        mode: Mode,
        key: &[u8],
        iv: Option<&[u8]>
        ) -> Crypter {
    let mut crypter = match Crypter::new(cipher, mode, key, iv) {
            Ok(val) => val,
            Err(err) => panic!("{}", err)
    };

    crypter.pad(false);

    crypter
}

fn main() {
    let args = App::new("aes_ecb")
                    .version("0.1")
                    .about("AES ECB/CBC tools")
                    .arg(Arg::with_name("key")
                            .short("k")
                            .long("key")
                            .value_name("KEY")
                            .takes_value(true)
                            .required(true))
                    .arg(Arg::with_name("mode")
                            .short("e")
                            .long("encrypt")
                            .help("encrypt (instead of decrypt)"))
                    .arg(Arg::with_name("iv")
                            .short("i")
                            .long("iv")
                            .value_name("INIT")
                            .takes_value(true)
                            .help("initial value"))
                    .get_matches();

    let mut buffer = String::new();

    io::stdin().read_to_string(&mut buffer).expect("aes_ecb");

    let decoded = match base64::decode(&buffer) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    let decoded_len = decoded.len();

    let cipher = Cipher::aes_128_ecb();

    let mode = match args.occurrences_of("mode") {
            0 => Mode::Decrypt,
            _ => Mode::Encrypt,
    };

    let key = match args.value_of("key") {
            Some(text) =>
                if text.len() == 16 {
                    text.as_bytes()
                } else {
                    panic!("invalid key length!");
                },
            None => panic!("no key provided.")
    };

    let iv = match args.value_of("iv") {
            Some(text) => Some(text.as_bytes()),
            None       => None,
    };

    let mut crypter = new_crypter_unpadded(cipher, mode, key, iv);
    let mut result  = vec![0u8; decoded_len + cipher.key_len()];

    let decrypted_len = match crypter.update(&decoded, result.as_mut_slice()) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    let output = &result[0..decrypted_len];

    match mode {
        Mode::Decrypt => println!("{}", String::from_utf8_lossy(output)),
        Mode::Encrypt => println!("{}", base64::encode(output)),
    };
}

<commit_msg>Exercises in better Rust code.<commit_after>
extern crate base64;
extern crate clap;
extern crate openssl;

use openssl::symm::{Cipher, Crypter, Mode};
use std::io::{self, Read};
use clap::{App, Arg};

fn crypt(cipher: Cipher,
         mode: Mode,
         key: &[u8],
         iv: Option<&[u8]>,
         input : Vec<u8>) -> Vec<u8> {

    let input_len   = input.len();

    let mut crypter = new_crypter_unpadded(cipher, mode, key, iv);
    let mut result  = vec![0u8; input_len + cipher.key_len()];

    let decrypted_len = match crypter.update(&input, result.as_mut_slice()) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    (&result[0..decrypted_len]).to_vec()
}

fn new_crypter_unpadded(
        cipher: Cipher,
        mode: Mode,
        key: &[u8],
        iv: Option<&[u8]>
        ) -> Crypter {

    let mut crypter = match Crypter::new(cipher, mode, key, iv) {
            Ok(val) => val,
            Err(err) => panic!("{}", err)
    };

    crypter.pad(false);

    crypter
}

fn main() {
    let args = App::new("aes_ecb")
                    .version("0.1")
                    .about("AES ECB/CBC tools")
                    .arg(Arg::with_name("key")
                            .short("k")
                            .long("key")
                            .value_name("KEY")
                            .takes_value(true)
                            .required(true))
                    .arg(Arg::with_name("mode")
                            .short("e")
                            .long("encrypt")
                            .help("encrypt (instead of decrypt)"))
                    .arg(Arg::with_name("iv")
                            .short("i")
                            .long("iv")
                            .value_name("INIT")
                            .takes_value(true)
                            .help("initial value"))
                    .get_matches();

    let mut buffer = String::new();

    io::stdin().read_to_string(&mut buffer).expect("aes_ecb");

    let decoded = match base64::decode(&buffer) {
            Ok(val)  => val,
            Err(err) => panic!("{}", err)
    };

    let mode = match args.occurrences_of("mode") {
            0 => Mode::Decrypt,
            _ => Mode::Encrypt,
    };

    let key = match args.value_of("key") {
            Some(text) =>
                if text.len() == 16 {
                    text.as_bytes()
                } else {
                    panic!("invalid key length!");
                },
            None => panic!("no key provided.")
    };

    let iv = match args.value_of("iv") {
            Some(text) => Some(text.as_bytes()),
            None       => None,
    };

    let cipher = Cipher::aes_128_ecb();

    let output = crypt(cipher, mode, key, iv, decoded);

    match mode {
        Mode::Decrypt => println!("{}", String::from_utf8_lossy(&output)),
        Mode::Encrypt => println!("{}", base64::encode(&output)),
    };
}

<|endoftext|>