<commit_before>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::arch::x86_64::{__cpuid, __cpuid_count};
use std::fmt::{self, Display};
use std::result;

use hypervisor::{Hypervisor, HypervisorCap, HypervisorX86_64, VcpuX86_64};

#[derive(Debug, PartialEq)]
pub enum Error {
    GetSupportedCpusFailed(base::Error),
    SetSupportedCpusFailed(base::Error),
}
pub type Result<T> = result::Result<T, Error>;

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match self {
            GetSupportedCpusFailed(e) => write!(f, "GetSupportedCpus ioctl failed: {}", e),
            SetSupportedCpusFailed(e) => write!(f, "SetSupportedCpus ioctl failed: {}", e),
        }
    }
}

// CPUID bits in ebx, ecx, and edx.
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
const EBX_CPU_COUNT_SHIFT: u32 = 16; // Index of this CPU.
const EBX_CPUID_SHIFT: u32 = 24; // Index of this CPU.
const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer
const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.

fn filter_cpuid(
    vcpu_id: usize,
    cpu_count: usize,
    cpuid: &mut hypervisor::CpuId,
    hypervisor: &impl Hypervisor,
) -> Result<()> {
    let entries = &mut cpuid.cpu_id_entries;

    for entry in entries {
        match entry.function {
            1 => {
                // X86 hypervisor feature
                if entry.index == 0 {
                    entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
                }
                if hypervisor.check_capability(&HypervisorCap::TscDeadlineTimer) {
                    entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
                }
                entry.ebx = (vcpu_id << EBX_CPUID_SHIFT) as u32
                    | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
                if cpu_count > 1 {
                    entry.ebx |= (cpu_count as u32) << EBX_CPU_COUNT_SHIFT;
                    entry.edx |= 1 << EDX_HTT_SHIFT;
                }
            }
            2 | 0x80000005 | 0x80000006 => unsafe {
                let result = __cpuid(entry.function);
                entry.eax = result.eax;
                entry.ebx = result.ebx;
                entry.ecx = result.ecx;
                entry.edx = result.edx;
            },
            4 => {
                unsafe {
                    let result = __cpuid_count(entry.function, entry.index);
                    entry.eax = result.eax;
                    entry.ebx = result.ebx;
                    entry.ecx = result.ecx;
                    entry.edx = result.edx;
                }
                entry.eax &= !0xFC000000;
            }
            6 => {
                // Clear X86 EPB feature.  No frequency selection in the hypervisor.
                entry.ecx &= !(1 << ECX_EPB_SHIFT);
            }
            _ => (),
        }
    }

    Ok(())
}

/// Sets up the cpuid entries for the given vcpu.  Can fail if there are too many CPUs specified or
/// if an ioctl returns an error.
///
/// # Arguments
///
/// * `hypervisor` - `HypervisorX86_64` impl for getting supported CPU IDs.
/// * `vcpu` - `VcpuX86_64` for setting CPU ID.
/// * `vcpu_id` - The vcpu index of `vcpu`.
/// * `nrcpus` - The number of vcpus being used by this VM.
pub fn setup_cpuid(
    hypervisor: &impl HypervisorX86_64,
    vcpu: &impl VcpuX86_64,
    vcpu_id: usize,
    nrcpus: usize,
) -> Result<()> {
    let mut cpuid = hypervisor
        .get_supported_cpuid()
        .map_err(Error::GetSupportedCpusFailed)?;

    filter_cpuid(vcpu_id, nrcpus, &mut cpuid, hypervisor)?;

    vcpu.set_cpuid(&cpuid)
        .map_err(Error::SetSupportedCpusFailed)
}

/// get host cpu max physical address bits
pub fn phy_max_address_bits() -> u32 {
    let mut phys_bits: u32 = 36;

    let highest_ext_function = unsafe { __cpuid(0x80000000) };
    if highest_ext_function.eax >= 0x80000008 {
        let addr_size = unsafe { __cpuid(0x80000008) };
        phys_bits = addr_size.eax & 0xff;
    }

    phys_bits
}

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

    #[test]
    fn feature_and_vendor_name() {
        let mut cpuid = hypervisor::CpuId::new(2);
        let hypervisor = hypervisor::kvm::Kvm::new().unwrap();

        let entries = &mut cpuid.cpu_id_entries;
        entries.push(CpuIdEntry {
            function: 0,
            ..Default::default()
        });
        entries.push(CpuIdEntry {
            function: 1,
            ecx: 0x10,
            edx: 0,
            ..Default::default()
        });
        assert_eq!(Ok(()), filter_cpuid(1, 2, &mut cpuid, &hypervisor));

        let entries = &mut cpuid.cpu_id_entries;
        assert_eq!(entries[0].function, 0);
        assert_eq!(1, (entries[1].ebx >> EBX_CPUID_SHIFT) & 0x000000ff);
        assert_eq!(2, (entries[1].ebx >> EBX_CPU_COUNT_SHIFT) & 0x000000ff);
        assert_eq!(
            EBX_CLFLUSH_CACHELINE,
            (entries[1].ebx >> EBX_CLFLUSH_SIZE_SHIFT) & 0x000000ff
        );
        assert_ne!(0, entries[1].ecx & (1 << ECX_HYPERVISOR_SHIFT));
        assert_ne!(0, entries[1].edx & (1 << EDX_HTT_SHIFT));
    }
}
<commit_msg>x86_64: set APIC ID in CPUID EAX=0xB leaf<commit_after>// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use std::arch::x86_64::{__cpuid, __cpuid_count};
use std::fmt::{self, Display};
use std::result;

use hypervisor::{Hypervisor, HypervisorCap, HypervisorX86_64, VcpuX86_64};

#[derive(Debug, PartialEq)]
pub enum Error {
    GetSupportedCpusFailed(base::Error),
    SetSupportedCpusFailed(base::Error),
}
pub type Result<T> = result::Result<T, Error>;

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::Error::*;

        match self {
            GetSupportedCpusFailed(e) => write!(f, "GetSupportedCpus ioctl failed: {}", e),
            SetSupportedCpusFailed(e) => write!(f, "SetSupportedCpus ioctl failed: {}", e),
        }
    }
}

// CPUID bits in ebx, ecx, and edx.
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
const EBX_CPU_COUNT_SHIFT: u32 = 16; // Index of this CPU.
const EBX_CPUID_SHIFT: u32 = 24; // Index of this CPU.
const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24; // TSC deadline mode of APIC timer
const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.

fn filter_cpuid(
    vcpu_id: usize,
    cpu_count: usize,
    cpuid: &mut hypervisor::CpuId,
    hypervisor: &impl Hypervisor,
) -> Result<()> {
    let entries = &mut cpuid.cpu_id_entries;

    for entry in entries {
        match entry.function {
            1 => {
                // X86 hypervisor feature
                if entry.index == 0 {
                    entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
                }
                if hypervisor.check_capability(&HypervisorCap::TscDeadlineTimer) {
                    entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
                }
                entry.ebx = (vcpu_id << EBX_CPUID_SHIFT) as u32
                    | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
                if cpu_count > 1 {
                    entry.ebx |= (cpu_count as u32) << EBX_CPU_COUNT_SHIFT;
                    entry.edx |= 1 << EDX_HTT_SHIFT;
                }
            }
            2 | 0x80000005 | 0x80000006 => unsafe {
                let result = __cpuid(entry.function);
                entry.eax = result.eax;
                entry.ebx = result.ebx;
                entry.ecx = result.ecx;
                entry.edx = result.edx;
            },
            4 => {
                unsafe {
                    let result = __cpuid_count(entry.function, entry.index);
                    entry.eax = result.eax;
                    entry.ebx = result.ebx;
                    entry.ecx = result.ecx;
                    entry.edx = result.edx;
                }
                entry.eax &= !0xFC000000;
            }
            6 => {
                // Clear X86 EPB feature.  No frequency selection in the hypervisor.
                entry.ecx &= !(1 << ECX_EPB_SHIFT);
            }
            0xB | 0x1F => {
                // Extended topology enumeration / V2 Extended topology enumeration
                // NOTE: these will need to be split if any of the fields that differ between
                // the two versions are to be set.
                entry.edx = vcpu_id as u32; // x2APIC ID
            }
            _ => (),
        }
    }

    Ok(())
}

/// Sets up the cpuid entries for the given vcpu.  Can fail if there are too many CPUs specified or
/// if an ioctl returns an error.
///
/// # Arguments
///
/// * `hypervisor` - `HypervisorX86_64` impl for getting supported CPU IDs.
/// * `vcpu` - `VcpuX86_64` for setting CPU ID.
/// * `vcpu_id` - The vcpu index of `vcpu`.
/// * `nrcpus` - The number of vcpus being used by this VM.
pub fn setup_cpuid(
    hypervisor: &impl HypervisorX86_64,
    vcpu: &impl VcpuX86_64,
    vcpu_id: usize,
    nrcpus: usize,
) -> Result<()> {
    let mut cpuid = hypervisor
        .get_supported_cpuid()
        .map_err(Error::GetSupportedCpusFailed)?;

    filter_cpuid(vcpu_id, nrcpus, &mut cpuid, hypervisor)?;

    vcpu.set_cpuid(&cpuid)
        .map_err(Error::SetSupportedCpusFailed)
}

/// get host cpu max physical address bits
pub fn phy_max_address_bits() -> u32 {
    let mut phys_bits: u32 = 36;

    let highest_ext_function = unsafe { __cpuid(0x80000000) };
    if highest_ext_function.eax >= 0x80000008 {
        let addr_size = unsafe { __cpuid(0x80000008) };
        phys_bits = addr_size.eax & 0xff;
    }

    phys_bits
}

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

    #[test]
    fn feature_and_vendor_name() {
        let mut cpuid = hypervisor::CpuId::new(2);
        let hypervisor = hypervisor::kvm::Kvm::new().unwrap();

        let entries = &mut cpuid.cpu_id_entries;
        entries.push(CpuIdEntry {
            function: 0,
            ..Default::default()
        });
        entries.push(CpuIdEntry {
            function: 1,
            ecx: 0x10,
            edx: 0,
            ..Default::default()
        });
        assert_eq!(Ok(()), filter_cpuid(1, 2, &mut cpuid, &hypervisor));

        let entries = &mut cpuid.cpu_id_entries;
        assert_eq!(entries[0].function, 0);
        assert_eq!(1, (entries[1].ebx >> EBX_CPUID_SHIFT) & 0x000000ff);
        assert_eq!(2, (entries[1].ebx >> EBX_CPU_COUNT_SHIFT) & 0x000000ff);
        assert_eq!(
            EBX_CLFLUSH_CACHELINE,
            (entries[1].ebx >> EBX_CLFLUSH_SIZE_SHIFT) & 0x000000ff
        );
        assert_ne!(0, entries[1].ecx & (1 << ECX_HYPERVISOR_SHIFT));
        assert_ne!(0, entries[1].edx & (1 << EDX_HTT_SHIFT));
    }
}
<|endoftext|>


<commit_before>//! Missing batteries for standard libraries.
use std::{cmp::Ordering, ops, time::Instant};

mod macros;
pub mod process;
pub mod panic_context;

pub use always_assert::{always, never};

#[inline(always)]
pub fn is_ci() -> bool {
    option_env!("CI").is_some()
}

#[must_use]
pub fn timeit(label: &'static str) -> impl Drop {
    struct Guard {
        label: &'static str,
        start: Instant,
    }

    impl Drop for Guard {
        fn drop(&mut self) {
            eprintln!("{}: {:.2?}", self.label, self.start.elapsed())
        }
    }

    Guard { label, start: Instant::now() }
}

/// Prints backtrace to stderr, useful for debugging.
#[cfg(feature = "backtrace")]
pub fn print_backtrace() {
    let bt = backtrace::Backtrace::new();
    eprintln!("{:?}", bt);
}
#[cfg(not(feature = "backtrace"))]
pub fn print_backtrace() {
    eprintln!(
        r#"Enable the backtrace feature.
Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
"#
    );
}

pub fn to_lower_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_lowercase)
}
pub fn to_upper_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_uppercase)
}
fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
    let mut buf = String::with_capacity(s.len());
    let mut prev = false;
    for c in s.chars() {
        // `&& prev` is required to not insert `_` before the first symbol.
        if c.is_ascii_uppercase() && prev {
            // This check is required to not translate `Weird_Case` into `weird__case`.
            if !buf.ends_with('_') {
                buf.push('_')
            }
        }
        prev = true;

        buf.push(change_case(&c));
    }
    buf
}

pub fn replace(buf: &mut String, from: char, to: &str) {
    if !buf.contains(from) {
        return;
    }
    // FIXME: do this in place.
    *buf = buf.replace(from, to)
}

// https://github.com/rust-lang/rust/issues/74773
pub fn split_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
    let mut split = haystack.splitn(2, delim);
    let prefix = split.next()?;
    let suffix = split.next()?;
    Some((prefix, suffix))
}
pub fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
    let mut split = haystack.rsplitn(2, delim);
    let suffix = split.next()?;
    let prefix = split.next()?;
    Some((prefix, suffix))
}

pub fn trim_indent(mut text: &str) -> String {
    if text.starts_with('\n') {
        text = &text[1..];
    }
    let indent = text
        .lines()
        .filter(|it| !it.trim().is_empty())
        .map(|it| it.len() - it.trim_start().len())
        .min()
        .unwrap_or(0);
    lines_with_ends(text)
        .map(
            |line| {
                if line.len() <= indent {
                    line.trim_start_matches(' ')
                } else {
                    &line[indent..]
                }
            },
        )
        .collect()
}

pub fn lines_with_ends(text: &str) -> LinesWithEnds {
    LinesWithEnds { text }
}

pub struct LinesWithEnds<'a> {
    text: &'a str,
}

impl<'a> Iterator for LinesWithEnds<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        if self.text.is_empty() {
            return None;
        }
        let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1);
        let (res, next) = self.text.split_at(idx);
        self.text = next;
        Some(res)
    }
}

/// Returns `idx` such that:
///
/// ```text
///     ∀ x in slice[..idx]:  pred(x)
///  && ∀ x in slice[idx..]: !pred(x)
/// ```
///
/// https://github.com/rust-lang/rust/issues/73831
pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
where
    P: FnMut(&T) -> bool,
{
    let mut left = 0;
    let mut right = slice.len();

    while left != right {
        let mid = left + (right - left) / 2;
        // SAFETY:
        // When left < right, left <= mid < right.
        // Therefore left always increases and right always decreases,
        // and either of them is selected.
        // In both cases left <= right is satisfied.
        // Therefore if left < right in a step,
        // left <= right is satisfied in the next step.
        // Therefore as long as left != right, 0 <= left < right <= len is satisfied
        // and if this case 0 <= mid < len is satisfied too.
        let value = unsafe { slice.get_unchecked(mid) };
        if pred(value) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }

    left
}

pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
where
    F: FnMut(&T) -> Ordering,
{
    let start = partition_point(slice, |it| key(it) == Ordering::Less);
    let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
    start..start + len
}

pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
    struct D<F: FnOnce()>(Option<F>);
    impl<F: FnOnce()> Drop for D<F> {
        fn drop(&mut self) {
            if let Some(f) = self.0.take() {
                f()
            }
        }
    }
    D(Some(f))
}

#[repr(transparent)]
pub struct JodChild(pub std::process::Child);

impl ops::Deref for JodChild {
    type Target = std::process::Child;
    fn deref(&self) -> &std::process::Child {
        &self.0
    }
}

impl ops::DerefMut for JodChild {
    fn deref_mut(&mut self) -> &mut std::process::Child {
        &mut self.0
    }
}

impl Drop for JodChild {
    fn drop(&mut self) {
        let _ = self.0.kill();
        let _ = self.0.wait();
    }
}

impl JodChild {
    pub fn into_inner(self) -> std::process::Child {
        // SAFETY: repr transparent
        unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
    }
}

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

    #[test]
    fn test_trim_indent() {
        assert_eq!(trim_indent(""), "");
        assert_eq!(
            trim_indent(
                "
            hello
            world
"
            ),
            "hello\nworld\n"
        );
        assert_eq!(
            trim_indent(
                "
            hello
            world"
            ),
            "hello\nworld"
        );
        assert_eq!(trim_indent("    hello\n    world\n"), "hello\nworld\n");
        assert_eq!(
            trim_indent(
                "
            fn main() {
                return 92;
            }
        "
            ),
            "fn main() {\n    return 92;\n}\n"
        );
    }
}
<commit_msg>minor: simplify<commit_after>//! Missing batteries for standard libraries.
use std::{cmp::Ordering, ops, time::Instant};

mod macros;
pub mod process;
pub mod panic_context;

pub use always_assert::{always, never};

#[inline(always)]
pub fn is_ci() -> bool {
    option_env!("CI").is_some()
}

#[must_use]
pub fn timeit(label: &'static str) -> impl Drop {
    let start = Instant::now();
    defer(move || eprintln!("{}: {:.2?}", label, start.elapsed()))
}

/// Prints backtrace to stderr, useful for debugging.
#[cfg(feature = "backtrace")]
pub fn print_backtrace() {
    let bt = backtrace::Backtrace::new();
    eprintln!("{:?}", bt);
}
#[cfg(not(feature = "backtrace"))]
pub fn print_backtrace() {
    eprintln!(
        r#"Enable the backtrace feature.
Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
"#
    );
}

pub fn to_lower_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_lowercase)
}
pub fn to_upper_snake_case(s: &str) -> String {
    to_snake_case(s, char::to_ascii_uppercase)
}
fn to_snake_case<F: Fn(&char) -> char>(s: &str, change_case: F) -> String {
    let mut buf = String::with_capacity(s.len());
    let mut prev = false;
    for c in s.chars() {
        // `&& prev` is required to not insert `_` before the first symbol.
        if c.is_ascii_uppercase() && prev {
            // This check is required to not translate `Weird_Case` into `weird__case`.
            if !buf.ends_with('_') {
                buf.push('_')
            }
        }
        prev = true;

        buf.push(change_case(&c));
    }
    buf
}

pub fn replace(buf: &mut String, from: char, to: &str) {
    if !buf.contains(from) {
        return;
    }
    // FIXME: do this in place.
    *buf = buf.replace(from, to)
}

// https://github.com/rust-lang/rust/issues/74773
pub fn split_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
    let mut split = haystack.splitn(2, delim);
    let prefix = split.next()?;
    let suffix = split.next()?;
    Some((prefix, suffix))
}
pub fn rsplit_once(haystack: &str, delim: char) -> Option<(&str, &str)> {
    let mut split = haystack.rsplitn(2, delim);
    let suffix = split.next()?;
    let prefix = split.next()?;
    Some((prefix, suffix))
}

pub fn trim_indent(mut text: &str) -> String {
    if text.starts_with('\n') {
        text = &text[1..];
    }
    let indent = text
        .lines()
        .filter(|it| !it.trim().is_empty())
        .map(|it| it.len() - it.trim_start().len())
        .min()
        .unwrap_or(0);
    lines_with_ends(text)
        .map(
            |line| {
                if line.len() <= indent {
                    line.trim_start_matches(' ')
                } else {
                    &line[indent..]
                }
            },
        )
        .collect()
}

pub fn lines_with_ends(text: &str) -> LinesWithEnds {
    LinesWithEnds { text }
}

pub struct LinesWithEnds<'a> {
    text: &'a str,
}

impl<'a> Iterator for LinesWithEnds<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        if self.text.is_empty() {
            return None;
        }
        let idx = self.text.find('\n').map_or(self.text.len(), |it| it + 1);
        let (res, next) = self.text.split_at(idx);
        self.text = next;
        Some(res)
    }
}

/// Returns `idx` such that:
///
/// ```text
///     ∀ x in slice[..idx]:  pred(x)
///  && ∀ x in slice[idx..]: !pred(x)
/// ```
///
/// https://github.com/rust-lang/rust/issues/73831
pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
where
    P: FnMut(&T) -> bool,
{
    let mut left = 0;
    let mut right = slice.len();

    while left != right {
        let mid = left + (right - left) / 2;
        // SAFETY:
        // When left < right, left <= mid < right.
        // Therefore left always increases and right always decreases,
        // and either of them is selected.
        // In both cases left <= right is satisfied.
        // Therefore if left < right in a step,
        // left <= right is satisfied in the next step.
        // Therefore as long as left != right, 0 <= left < right <= len is satisfied
        // and if this case 0 <= mid < len is satisfied too.
        let value = unsafe { slice.get_unchecked(mid) };
        if pred(value) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }

    left
}

pub fn equal_range_by<T, F>(slice: &[T], mut key: F) -> ops::Range<usize>
where
    F: FnMut(&T) -> Ordering,
{
    let start = partition_point(slice, |it| key(it) == Ordering::Less);
    let len = partition_point(&slice[start..], |it| key(it) == Ordering::Equal);
    start..start + len
}

#[must_use]
pub fn defer<F: FnOnce()>(f: F) -> impl Drop {
    struct D<F: FnOnce()>(Option<F>);
    impl<F: FnOnce()> Drop for D<F> {
        fn drop(&mut self) {
            if let Some(f) = self.0.take() {
                f()
            }
        }
    }
    D(Some(f))
}

#[repr(transparent)]
pub struct JodChild(pub std::process::Child);

impl ops::Deref for JodChild {
    type Target = std::process::Child;
    fn deref(&self) -> &std::process::Child {
        &self.0
    }
}

impl ops::DerefMut for JodChild {
    fn deref_mut(&mut self) -> &mut std::process::Child {
        &mut self.0
    }
}

impl Drop for JodChild {
    fn drop(&mut self) {
        let _ = self.0.kill();
        let _ = self.0.wait();
    }
}

impl JodChild {
    pub fn into_inner(self) -> std::process::Child {
        // SAFETY: repr transparent
        unsafe { std::mem::transmute::<JodChild, std::process::Child>(self) }
    }
}

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

    #[test]
    fn test_trim_indent() {
        assert_eq!(trim_indent(""), "");
        assert_eq!(
            trim_indent(
                "
            hello
            world
"
            ),
            "hello\nworld\n"
        );
        assert_eq!(
            trim_indent(
                "
            hello
            world"
            ),
            "hello\nworld"
        );
        assert_eq!(trim_indent("    hello\n    world\n"), "hello\nworld\n");
        assert_eq!(
            trim_indent(
                "
            fn main() {
                return 92;
            }
        "
            ),
            "fn main() {\n    return 92;\n}\n"
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2019, 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RuntimeSettings {
    pub polling: RuntimePolling,
    pub update: RuntimeUpdate,
    pub path: PathBuf,
    pub persistent: bool,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RuntimePolling {
    pub last: DateTime<Utc>,
    pub retries: usize,
    pub now: bool,
    pub server_address: ServerAddress,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ServerAddress {
    Default,
    Custom(String),
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RuntimeUpdate {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub upgrade_to_installation: Option<InstallationSet>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub applied_package_uid: Option<String>,
}

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum InstallationSet {
    A,
    B,
}
<commit_msg>sdk: Use lowercase for InstallationSet ser/des<commit_after>// Copyright (C) 2019, 2020 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RuntimeSettings {
    pub polling: RuntimePolling,
    pub update: RuntimeUpdate,
    pub path: PathBuf,
    pub persistent: bool,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RuntimePolling {
    pub last: DateTime<Utc>,
    pub retries: usize,
    pub now: bool,
    pub server_address: ServerAddress,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum ServerAddress {
    Default,
    Custom(String),
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct RuntimeUpdate {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub upgrade_to_installation: Option<InstallationSet>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub applied_package_uid: Option<String>,
}

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum InstallationSet {
    A,
    B,
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    diagnostic_reporter::{get_diagnostics_data, DiagnosticReporter},
    js_language_server::JSLanguageServer,
    lsp_runtime_error::LSPRuntimeResult,
    node_resolution_info::{get_node_resolution_info, NodeResolutionInfo},
    utils::extract_project_name_from_url,
    utils::{
        extract_executable_definitions_from_text_document, extract_executable_document_from_text,
    },
};
use crate::{LSPExtraDataProvider, LSPRuntimeError};
use common::{Diagnostic as CompilerDiagnostic, PerfLogger, SourceLocationKey, Span};
use crossbeam::channel::{SendError, Sender};
use dashmap::{mapref::entry::Entry, DashMap};
use fnv::FnvBuildHasher;
use graphql_ir::{
    build_ir_with_extra_features, BuilderOptions, FragmentVariablesSemantic, Program,
};
use graphql_syntax::{
    parse_executable_with_error_recovery, ExecutableDefinition, ExecutableDocument, GraphQLSource,
};
use interner::{Intern, StringKey};
use log::debug;
use lsp_server::Message;
use lsp_types::{Diagnostic, DiagnosticTag, Range, TextDocumentPositionParams, Url};
use relay_compiler::{
    config::{Config, ProjectConfig},
    FileCategorizer,
};
use relay_transforms::deprecated_fields_for_executable_definition;
use schema::SDLSchema;
use schema_documentation::{
    CombinedSchemaDocumentation, SchemaDocumentation, SchemaDocumentationLoader,
};
use std::{path::PathBuf, sync::Arc};
use tokio::sync::Notify;

pub type Schemas = Arc<DashMap<StringKey, Arc<SDLSchema>, FnvBuildHasher>>;
pub type SourcePrograms = Arc<DashMap<StringKey, Program, FnvBuildHasher>>;
pub type ProjectStatusMap = Arc<DashMap<StringKey, ProjectStatus, FnvBuildHasher>>;

#[derive(Eq, PartialEq)]
pub enum ProjectStatus {
    Activated,
    Completed,
}

pub trait GlobalState {
    type TSchemaDocumentation: SchemaDocumentation;

    fn get_config(&self) -> Arc<Config>;

    fn get_schema(&self, project_name: &StringKey) -> LSPRuntimeResult<Arc<SDLSchema>>;

    fn get_program(&self, project_name: &StringKey) -> LSPRuntimeResult<Program>;

    fn resolve_node(
        &self,
        text_document_position: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo>;

    fn root_dir(&self) -> PathBuf;

    fn process_synced_sources(
        &self,
        uri: &Url,
        sources: Vec<GraphQLSource>,
    ) -> LSPRuntimeResult<()>;

    fn remove_synced_sources(&self, url: &Url);

    fn extract_executable_document_from_text(
        &self,
        position: &TextDocumentPositionParams,
        index_offset: usize,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span)>;

    fn get_schema_documentation(&self, schema_name: &str) -> Self::TSchemaDocumentation;

    fn get_extra_data_provider(&self) -> &dyn LSPExtraDataProvider;

    fn resolve_executable_definitions(
        &self,
        text_document_uri: &Url,
    ) -> LSPRuntimeResult<Vec<ExecutableDefinition>>;

    fn get_diagnostic_for_range(&self, url: &Url, range: Range) -> Option<Diagnostic>;

    /// For Relay - project_name is an human-readable identifier of a set of configurations,
    /// source files, schema extensions, etc, that are compiled together using a single GraphQL
    /// Schema. project_name typically the same as the schema name: facebook, intern, etc.
    /// For Native - it may be a BuildConfigName.
    fn extract_project_name_from_url(&self, url: &Url) -> LSPRuntimeResult<StringKey>;

    // Experimental (Relay-only) JS Language Server instance
    fn get_js_language_sever(&self) -> Option<&dyn JSLanguageServer<TState = Self>>;
}

/// This structure contains all available resources that we may use in the Relay LSP message/notification
/// handlers. Such as schema, programs, extra_data_providers, etc...
pub struct LSPState<
    TPerfLogger: PerfLogger + 'static,
    TSchemaDocumentation: SchemaDocumentation + 'static,
> {
    pub config: Arc<Config>,
    root_dir: PathBuf,
    pub extra_data_provider: Box<dyn LSPExtraDataProvider>,
    file_categorizer: FileCategorizer,
    schemas: Schemas,
    schema_documentation_loader: Option<Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>>,
    pub source_programs: SourcePrograms,
    synced_graphql_documents: DashMap<Url, Vec<GraphQLSource>>,
    pub perf_logger: Arc<TPerfLogger>,
    pub diagnostic_reporter: Arc<DiagnosticReporter>,
    pub notify_sender: Arc<Notify>,
    pub sender: Sender<Message>,
    pub project_status: ProjectStatusMap,
    pub js_resource: Option<Box<dyn JSLanguageServer<TState = Self>>>,
}

impl<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation>
    LSPState<TPerfLogger, TSchemaDocumentation>
{
    /// Private constructor
    fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Option<Box<dyn JSLanguageServer<TState = Self>>>,
        sender: Sender<Message>,
    ) -> Self {
        let file_categorizer = FileCategorizer::from_config(&config);
        let root_dir = &config.root_dir.clone();
        let diagnostic_reporter = Arc::new(DiagnosticReporter::new(
            config.root_dir.clone(),
            sender.clone(),
        ));

        Self {
            config,
            diagnostic_reporter,
            extra_data_provider,
            file_categorizer,
            notify_sender: Arc::new(Notify::new()),
            perf_logger,
            project_status: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            root_dir: root_dir.clone(),
            schemas: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            schema_documentation_loader,
            source_programs: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            synced_graphql_documents: Default::default(),
            js_resource,
            sender,
        }
    }

    /// This method is responsible for creating schema/source_programs for LSP internal state
    /// - so the LSP can provide these to Hover/Completion/GoToDefinition requests.
    /// It also creates a watchman subscription that is responsible for keeping these resources up-to-date.
    pub(crate) fn create_state(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Option<Box<dyn JSLanguageServer<TState = Self>>>,
        sender: Sender<Message>,
    ) -> Self {
        debug!("Creating lsp_state...");
        let lsp_state = Self::new(
            config,
            perf_logger,
            extra_data_provider,
            schema_documentation_loader,
            js_resource,
            sender.clone(),
        );

        // Preload schema documentation - this will warm-up schema documentation cache in the LSP Extra Data providers
        // TODO: Find a better place for this
        lsp_state.preload_documentation();

        debug!("Creating lsp_state created!");
        lsp_state
    }

    pub fn get_schemas(&self) -> Schemas {
        self.schemas.clone()
    }

    fn insert_synced_sources(&self, url: &Url, sources: Vec<GraphQLSource>) {
        self.synced_graphql_documents.insert(url.clone(), sources);
    }

    fn validate_synced_sources(&self, url: &Url) -> LSPRuntimeResult<()> {
        let mut diagnostics = vec![];
        let graphql_sources = self.synced_graphql_documents.get(url).ok_or_else(|| {
            LSPRuntimeError::UnexpectedError(format!("Expected GraphQL sources for URL {}", url))
        })?;
        let project_name = self.extract_project_name_from_url(url)?;
        let schema = self
            .schemas
            .get(&project_name)
            .ok_or(LSPRuntimeError::ExpectedError)?;

        for graphql_source in graphql_sources.iter() {
            let result = parse_executable_with_error_recovery(
                &graphql_source.text,
                SourceLocationKey::standalone(&url.to_string()),
            );

            diagnostics.extend(
                result
                    .errors
                    .iter()
                    .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
            );

            let compiler_diagnostics = match build_ir_with_extra_features(
                &schema,
                &result.item.definitions,
                BuilderOptions {
                    allow_undefined_fragment_spreads: true,
                    fragment_variables_semantic: FragmentVariablesSemantic::PassedValue,
                    relay_mode: true,
                    default_anonymous_operation_name: None,
                },
            )
            .and_then(|documents| {
                // Waiting until T101267297 is resolved to enable these.
                if true {
                    return Ok(vec![]);
                }
                let mut warnings = vec![];
                for document in documents {
                    // Today the only warning we check for is deprecated
                    // fields, but in the future we could check for more
                    // things here by making this more generic.
                    warnings.extend(deprecated_fields_for_executable_definition(
                        &schema, &document,
                    )?)
                }
                Ok(warnings)
            }) {
                Ok(warnings) => warnings,
                Err(errors) => errors,
            };

            diagnostics.extend(
                compiler_diagnostics
                    .iter()
                    .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
            );
        }
        self.diagnostic_reporter
            .update_quick_diagnostics_for_url(url, diagnostics);

        Ok(())
    }

    fn preload_documentation(&self) {
        for project_config in self.config.enabled_projects() {
            self.get_schema_documentation(&project_config.name.to_string());
        }
    }

    pub fn get_project_config_ref(&self, project_name: StringKey) -> Option<&ProjectConfig> {
        self.config
            .enabled_projects()
            .find(|project_config| project_config.name == project_name)
    }

    pub fn get_logger(&self) -> Arc<TPerfLogger> {
        self.perf_logger.clone()
    }

    pub fn send_message(&self, message: Message) -> Result<(), SendError<Message>> {
        self.sender.send(message)
    }
}

pub fn convert_diagnostic(
    graphql_source: &GraphQLSource,
    diagnostic: &CompilerDiagnostic,
) -> Diagnostic {
    let tags: Vec<DiagnosticTag> = diagnostic.tags();

    Diagnostic {
        code: None,
        data: get_diagnostics_data(&diagnostic),
        message: diagnostic.message().to_string(),
        range: diagnostic.location().span().to_range(
            &graphql_source.text,
            graphql_source.line_index,
            graphql_source.column_index,
        ),
        related_information: None,
        severity: Some(diagnostic.severity()),
        tags: if tags.len() == 0 { None } else { Some(tags) },
        source: None,
        ..Default::default()
    }
}

impl<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation + 'static>
    GlobalState for LSPState<TPerfLogger, TSchemaDocumentation>
{
    type TSchemaDocumentation =
        CombinedSchemaDocumentation<Option<Arc<SDLSchema>>, Option<Arc<TSchemaDocumentation>>>;

    fn get_config(&self) -> Arc<Config> {
        self.config.clone()
    }

    fn root_dir(&self) -> PathBuf {
        self.root_dir.clone()
    }

    fn get_schema(&self, project_name: &StringKey) -> LSPRuntimeResult<Arc<SDLSchema>> {
        self.schemas
            .get(project_name)
            .as_deref()
            .cloned()
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "get_schema: schema is missing (or not ready, yet) for the `{}` project.",
                    project_name
                ))
            })
    }

    fn get_program(&self, project_name: &StringKey) -> LSPRuntimeResult<Program> {
        self.source_programs
            .get(project_name)
            .map(|p| p.value().clone())
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "get_program: program is missing (or not ready, yet) for the `{}` project.",
                    project_name
                ))
            })
    }

    fn resolve_node(
        &self,
        text_document_position: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo> {
        get_node_resolution_info(
            text_document_position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
        )
    }

    fn process_synced_sources(
        &self,
        uri: &Url,
        sources: Vec<GraphQLSource>,
    ) -> LSPRuntimeResult<()> {
        let project_name = self.extract_project_name_from_url(uri)?;

        if let Entry::Vacant(e) = self.project_status.entry(project_name) {
            e.insert(ProjectStatus::Activated);
            self.notify_sender.notify_one();
        }

        self.insert_synced_sources(uri, sources);
        self.validate_synced_sources(uri)?;

        Ok(())
    }

    fn remove_synced_sources(&self, url: &Url) {
        self.synced_graphql_documents.remove(url);
        self.diagnostic_reporter
            .clear_quick_diagnostics_for_url(url);
    }

    fn extract_executable_document_from_text(
        &self,
        position: &TextDocumentPositionParams,
        index_offset: usize,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span)> {
        extract_executable_document_from_text(
            &self.synced_graphql_documents,
            position,
            index_offset,
        )
    }

    fn get_schema_documentation(&self, schema_name: &str) -> Self::TSchemaDocumentation {
        let primary = self
            .schemas
            .get(&schema_name.intern())
            .map(|x| Arc::clone(x.value()));
        let secondary = self
            .schema_documentation_loader
            .as_ref()
            .map(|loader| loader.get_schema_documentation(schema_name));

        CombinedSchemaDocumentation::new(primary, secondary)
    }

    fn extract_project_name_from_url(&self, url: &Url) -> LSPRuntimeResult<StringKey> {
        extract_project_name_from_url(&self.file_categorizer, url, &self.root_dir)
    }

    fn get_extra_data_provider(&self) -> &dyn LSPExtraDataProvider {
        self.extra_data_provider.as_ref()
    }

    fn resolve_executable_definitions(
        &self,
        text_document_uri: &Url,
    ) -> LSPRuntimeResult<Vec<ExecutableDefinition>> {
        extract_executable_definitions_from_text_document(
            text_document_uri,
            &self.synced_graphql_documents,
        )
    }

    fn get_diagnostic_for_range(&self, url: &Url, range: Range) -> Option<Diagnostic> {
        self.diagnostic_reporter
            .get_diagnostics_for_range(url, range)
    }

    fn get_js_language_sever(&self) -> Option<&dyn JSLanguageServer<TState = Self>> {
        self.js_resource.as_deref()
    }
}
<commit_msg>Remove `get_config` method on GlobalState<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    diagnostic_reporter::{get_diagnostics_data, DiagnosticReporter},
    js_language_server::JSLanguageServer,
    lsp_runtime_error::LSPRuntimeResult,
    node_resolution_info::{get_node_resolution_info, NodeResolutionInfo},
    utils::extract_project_name_from_url,
    utils::{
        extract_executable_definitions_from_text_document, extract_executable_document_from_text,
    },
};
use crate::{LSPExtraDataProvider, LSPRuntimeError};
use common::{Diagnostic as CompilerDiagnostic, PerfLogger, SourceLocationKey, Span};
use crossbeam::channel::{SendError, Sender};
use dashmap::{mapref::entry::Entry, DashMap};
use fnv::FnvBuildHasher;
use graphql_ir::{
    build_ir_with_extra_features, BuilderOptions, FragmentVariablesSemantic, Program,
};
use graphql_syntax::{
    parse_executable_with_error_recovery, ExecutableDefinition, ExecutableDocument, GraphQLSource,
};
use interner::{Intern, StringKey};
use log::debug;
use lsp_server::Message;
use lsp_types::{Diagnostic, DiagnosticTag, Range, TextDocumentPositionParams, Url};
use relay_compiler::{
    config::{Config, ProjectConfig},
    FileCategorizer,
};
use relay_transforms::deprecated_fields_for_executable_definition;
use schema::SDLSchema;
use schema_documentation::{
    CombinedSchemaDocumentation, SchemaDocumentation, SchemaDocumentationLoader,
};
use std::{path::PathBuf, sync::Arc};
use tokio::sync::Notify;

pub type Schemas = Arc<DashMap<StringKey, Arc<SDLSchema>, FnvBuildHasher>>;
pub type SourcePrograms = Arc<DashMap<StringKey, Program, FnvBuildHasher>>;
pub type ProjectStatusMap = Arc<DashMap<StringKey, ProjectStatus, FnvBuildHasher>>;

#[derive(Eq, PartialEq)]
pub enum ProjectStatus {
    Activated,
    Completed,
}

pub trait GlobalState {
    type TSchemaDocumentation: SchemaDocumentation;

    fn get_schema(&self, project_name: &StringKey) -> LSPRuntimeResult<Arc<SDLSchema>>;

    fn get_program(&self, project_name: &StringKey) -> LSPRuntimeResult<Program>;

    fn resolve_node(
        &self,
        text_document_position: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo>;

    fn root_dir(&self) -> PathBuf;

    fn process_synced_sources(
        &self,
        uri: &Url,
        sources: Vec<GraphQLSource>,
    ) -> LSPRuntimeResult<()>;

    fn remove_synced_sources(&self, url: &Url);

    fn extract_executable_document_from_text(
        &self,
        position: &TextDocumentPositionParams,
        index_offset: usize,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span)>;

    fn get_schema_documentation(&self, schema_name: &str) -> Self::TSchemaDocumentation;

    fn get_extra_data_provider(&self) -> &dyn LSPExtraDataProvider;

    fn resolve_executable_definitions(
        &self,
        text_document_uri: &Url,
    ) -> LSPRuntimeResult<Vec<ExecutableDefinition>>;

    fn get_diagnostic_for_range(&self, url: &Url, range: Range) -> Option<Diagnostic>;

    /// For Relay - project_name is an human-readable identifier of a set of configurations,
    /// source files, schema extensions, etc, that are compiled together using a single GraphQL
    /// Schema. project_name typically the same as the schema name: facebook, intern, etc.
    /// For Native - it may be a BuildConfigName.
    fn extract_project_name_from_url(&self, url: &Url) -> LSPRuntimeResult<StringKey>;

    // Experimental (Relay-only) JS Language Server instance
    fn get_js_language_sever(&self) -> Option<&dyn JSLanguageServer<TState = Self>>;
}

/// This structure contains all available resources that we may use in the Relay LSP message/notification
/// handlers. Such as schema, programs, extra_data_providers, etc...
pub struct LSPState<
    TPerfLogger: PerfLogger + 'static,
    TSchemaDocumentation: SchemaDocumentation + 'static,
> {
    pub config: Arc<Config>,
    root_dir: PathBuf,
    pub extra_data_provider: Box<dyn LSPExtraDataProvider>,
    file_categorizer: FileCategorizer,
    schemas: Schemas,
    schema_documentation_loader: Option<Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>>,
    pub source_programs: SourcePrograms,
    synced_graphql_documents: DashMap<Url, Vec<GraphQLSource>>,
    pub perf_logger: Arc<TPerfLogger>,
    pub diagnostic_reporter: Arc<DiagnosticReporter>,
    pub notify_sender: Arc<Notify>,
    pub sender: Sender<Message>,
    pub project_status: ProjectStatusMap,
    pub js_resource: Option<Box<dyn JSLanguageServer<TState = Self>>>,
}

impl<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation>
    LSPState<TPerfLogger, TSchemaDocumentation>
{
    /// Private constructor
    fn new(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Option<Box<dyn JSLanguageServer<TState = Self>>>,
        sender: Sender<Message>,
    ) -> Self {
        let file_categorizer = FileCategorizer::from_config(&config);
        let root_dir = &config.root_dir.clone();
        let diagnostic_reporter = Arc::new(DiagnosticReporter::new(
            config.root_dir.clone(),
            sender.clone(),
        ));

        Self {
            config,
            diagnostic_reporter,
            extra_data_provider,
            file_categorizer,
            notify_sender: Arc::new(Notify::new()),
            perf_logger,
            project_status: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            root_dir: root_dir.clone(),
            schemas: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            schema_documentation_loader,
            source_programs: Arc::new(DashMap::with_hasher(FnvBuildHasher::default())),
            synced_graphql_documents: Default::default(),
            js_resource,
            sender,
        }
    }

    /// This method is responsible for creating schema/source_programs for LSP internal state
    /// - so the LSP can provide these to Hover/Completion/GoToDefinition requests.
    /// It also creates a watchman subscription that is responsible for keeping these resources up-to-date.
    pub(crate) fn create_state(
        config: Arc<Config>,
        perf_logger: Arc<TPerfLogger>,
        extra_data_provider: Box<dyn LSPExtraDataProvider>,
        schema_documentation_loader: Option<
            Box<dyn SchemaDocumentationLoader<TSchemaDocumentation>>,
        >,
        js_resource: Option<Box<dyn JSLanguageServer<TState = Self>>>,
        sender: Sender<Message>,
    ) -> Self {
        debug!("Creating lsp_state...");
        let lsp_state = Self::new(
            config,
            perf_logger,
            extra_data_provider,
            schema_documentation_loader,
            js_resource,
            sender.clone(),
        );

        // Preload schema documentation - this will warm-up schema documentation cache in the LSP Extra Data providers
        // TODO: Find a better place for this
        lsp_state.preload_documentation();

        debug!("Creating lsp_state created!");
        lsp_state
    }

    pub fn get_schemas(&self) -> Schemas {
        self.schemas.clone()
    }

    fn insert_synced_sources(&self, url: &Url, sources: Vec<GraphQLSource>) {
        self.synced_graphql_documents.insert(url.clone(), sources);
    }

    fn validate_synced_sources(&self, url: &Url) -> LSPRuntimeResult<()> {
        let mut diagnostics = vec![];
        let graphql_sources = self.synced_graphql_documents.get(url).ok_or_else(|| {
            LSPRuntimeError::UnexpectedError(format!("Expected GraphQL sources for URL {}", url))
        })?;
        let project_name = self.extract_project_name_from_url(url)?;
        let schema = self
            .schemas
            .get(&project_name)
            .ok_or(LSPRuntimeError::ExpectedError)?;

        for graphql_source in graphql_sources.iter() {
            let result = parse_executable_with_error_recovery(
                &graphql_source.text,
                SourceLocationKey::standalone(&url.to_string()),
            );

            diagnostics.extend(
                result
                    .errors
                    .iter()
                    .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
            );

            let compiler_diagnostics = match build_ir_with_extra_features(
                &schema,
                &result.item.definitions,
                BuilderOptions {
                    allow_undefined_fragment_spreads: true,
                    fragment_variables_semantic: FragmentVariablesSemantic::PassedValue,
                    relay_mode: true,
                    default_anonymous_operation_name: None,
                },
            )
            .and_then(|documents| {
                // Waiting until T101267297 is resolved to enable these.
                if true {
                    return Ok(vec![]);
                }
                let mut warnings = vec![];
                for document in documents {
                    // Today the only warning we check for is deprecated
                    // fields, but in the future we could check for more
                    // things here by making this more generic.
                    warnings.extend(deprecated_fields_for_executable_definition(
                        &schema, &document,
                    )?)
                }
                Ok(warnings)
            }) {
                Ok(warnings) => warnings,
                Err(errors) => errors,
            };

            diagnostics.extend(
                compiler_diagnostics
                    .iter()
                    .map(|diagnostic| convert_diagnostic(graphql_source, diagnostic)),
            );
        }
        self.diagnostic_reporter
            .update_quick_diagnostics_for_url(url, diagnostics);

        Ok(())
    }

    fn preload_documentation(&self) {
        for project_config in self.config.enabled_projects() {
            self.get_schema_documentation(&project_config.name.to_string());
        }
    }

    pub fn get_project_config_ref(&self, project_name: StringKey) -> Option<&ProjectConfig> {
        self.config
            .enabled_projects()
            .find(|project_config| project_config.name == project_name)
    }

    pub fn get_logger(&self) -> Arc<TPerfLogger> {
        self.perf_logger.clone()
    }

    pub fn send_message(&self, message: Message) -> Result<(), SendError<Message>> {
        self.sender.send(message)
    }
}

pub fn convert_diagnostic(
    graphql_source: &GraphQLSource,
    diagnostic: &CompilerDiagnostic,
) -> Diagnostic {
    let tags: Vec<DiagnosticTag> = diagnostic.tags();

    Diagnostic {
        code: None,
        data: get_diagnostics_data(&diagnostic),
        message: diagnostic.message().to_string(),
        range: diagnostic.location().span().to_range(
            &graphql_source.text,
            graphql_source.line_index,
            graphql_source.column_index,
        ),
        related_information: None,
        severity: Some(diagnostic.severity()),
        tags: if tags.len() == 0 { None } else { Some(tags) },
        source: None,
        ..Default::default()
    }
}

impl<TPerfLogger: PerfLogger + 'static, TSchemaDocumentation: SchemaDocumentation + 'static>
    GlobalState for LSPState<TPerfLogger, TSchemaDocumentation>
{
    type TSchemaDocumentation =
        CombinedSchemaDocumentation<Option<Arc<SDLSchema>>, Option<Arc<TSchemaDocumentation>>>;

    fn root_dir(&self) -> PathBuf {
        self.root_dir.clone()
    }

    fn get_schema(&self, project_name: &StringKey) -> LSPRuntimeResult<Arc<SDLSchema>> {
        self.schemas
            .get(project_name)
            .as_deref()
            .cloned()
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "get_schema: schema is missing (or not ready, yet) for the `{}` project.",
                    project_name
                ))
            })
    }

    fn get_program(&self, project_name: &StringKey) -> LSPRuntimeResult<Program> {
        self.source_programs
            .get(project_name)
            .map(|p| p.value().clone())
            .ok_or_else(|| {
                LSPRuntimeError::UnexpectedError(format!(
                    "get_program: program is missing (or not ready, yet) for the `{}` project.",
                    project_name
                ))
            })
    }

    fn resolve_node(
        &self,
        text_document_position: &TextDocumentPositionParams,
    ) -> LSPRuntimeResult<NodeResolutionInfo> {
        get_node_resolution_info(
            text_document_position,
            &self.synced_graphql_documents,
            &self.file_categorizer,
            &self.root_dir,
        )
    }

    fn process_synced_sources(
        &self,
        uri: &Url,
        sources: Vec<GraphQLSource>,
    ) -> LSPRuntimeResult<()> {
        let project_name = self.extract_project_name_from_url(uri)?;

        if let Entry::Vacant(e) = self.project_status.entry(project_name) {
            e.insert(ProjectStatus::Activated);
            self.notify_sender.notify_one();
        }

        self.insert_synced_sources(uri, sources);
        self.validate_synced_sources(uri)?;

        Ok(())
    }

    fn remove_synced_sources(&self, url: &Url) {
        self.synced_graphql_documents.remove(url);
        self.diagnostic_reporter
            .clear_quick_diagnostics_for_url(url);
    }

    fn extract_executable_document_from_text(
        &self,
        position: &TextDocumentPositionParams,
        index_offset: usize,
    ) -> LSPRuntimeResult<(ExecutableDocument, Span)> {
        extract_executable_document_from_text(
            &self.synced_graphql_documents,
            position,
            index_offset,
        )
    }

    fn get_schema_documentation(&self, schema_name: &str) -> Self::TSchemaDocumentation {
        let primary = self
            .schemas
            .get(&schema_name.intern())
            .map(|x| Arc::clone(x.value()));
        let secondary = self
            .schema_documentation_loader
            .as_ref()
            .map(|loader| loader.get_schema_documentation(schema_name));

        CombinedSchemaDocumentation::new(primary, secondary)
    }

    fn extract_project_name_from_url(&self, url: &Url) -> LSPRuntimeResult<StringKey> {
        extract_project_name_from_url(&self.file_categorizer, url, &self.root_dir)
    }

    fn get_extra_data_provider(&self) -> &dyn LSPExtraDataProvider {
        self.extra_data_provider.as_ref()
    }

    fn resolve_executable_definitions(
        &self,
        text_document_uri: &Url,
    ) -> LSPRuntimeResult<Vec<ExecutableDefinition>> {
        extract_executable_definitions_from_text_document(
            text_document_uri,
            &self.synced_graphql_documents,
        )
    }

    fn get_diagnostic_for_range(&self, url: &Url, range: Range) -> Option<Diagnostic> {
        self.diagnostic_reporter
            .get_diagnostics_for_range(url, range)
    }

    fn get_js_language_sever(&self) -> Option<&dyn JSLanguageServer<TState = Self>> {
        self.js_resource.as_deref()
    }
}
<|endoftext|>


<commit_before>/// Internal namespace.
pub( crate ) mod private
{
  use former::Former;
  use crate::string::split::*;
  use std::collections::HashMap;

  ///
  /// Wrapper types to make transformation.
  ///

  #[ derive( Debug, Clone, PartialEq ) ]
  pub enum OpType<T>
  {
    /// Wrapper over single element of type <T>.
    Primitive( T ),
    /// Wrapper over vector of elements of type <T>.
    Vector( Vec<T> ),
    /// Wrapper over hash map of elements of type <T>.
    Map( HashMap<String, T> ),
  }

  impl<T : Default> Default for OpType<T>
  {
    fn default() -> Self
    {
      OpType::Primitive( T::default() )
    }
  }

  impl<T> From<T> for OpType<T>
  {
    fn from( value: T ) -> Self
    {
      OpType::Primitive( value )
    }
  }

  impl<T> From<Vec<T>> for OpType<T>
  {
    fn from( value: Vec<T> ) -> Self
    {
      OpType::Vector( value )
    }
  }

  impl<T> Into<Vec<T>> for OpType<T>
  {
    fn into( self ) -> Vec<T>
    {
      match self
      {
        OpType::Vector( vec ) => vec,
        _ => unimplemented!( "not implemented" ),
      }
    }
  }

  impl<T : Clone> OpType<T>
  {
    /// Append item of OpType to current value. If current type is `Primitive`, then it will be converted to
    /// `Vector`.
    pub fn append( mut self, item : OpType<T> ) -> OpType<T>
    {
      let mut mut_item = item;
      match self
      {
        OpType::Primitive( value ) =>
        {
          match mut_item
          {
            OpType::Primitive( ins ) =>
            {
              let vector = vec![ value, ins ];
              OpType::Vector( vector )
            }
            OpType::Vector( ref mut vector ) =>
            {
              vector.insert( 0, value );
              mut_item
            },
            OpType::Map( _ ) => panic!( "Unexpected operation. Please, use method `insert` to insert item in hash map." ),
          }
        },
        OpType::Vector( ref mut vector ) =>
        {
          match mut_item
          {
            OpType::Primitive( ins ) =>
            {
              vector.push( ins );
              self
            }
            OpType::Vector( ref mut ins_vec ) =>
            {
              vector.append( ins_vec );
              self
            },
            OpType::Map( _ ) => panic!( "Unexpected operation. Please, use method `insert` to insert item in hash map." ),
          }
        },
        OpType::Map( _ ) => panic!( "Unexpected operation. Please, use method `insert` to insert item in hash map." ),
      }
    }

    /// Unwrap primitive value. Consumes self.
    pub fn primitive( self ) -> Option<T>
    {
      match self
      {
        OpType::Primitive( v ) => Some( v ),
        _ => None,
      }
    }

    /// Unwrap vector value. Consumes self.
    pub fn vector( self ) -> Option<Vec<T>>
    {
      match self
      {
        OpType::Vector( vec ) => Some( vec ),
        _ => None,
      }
    }
  }

  ///
  /// Parsed request data.
  ///

  #[ allow( dead_code ) ]
  #[ derive( Debug, Default, PartialEq ) ]
  pub struct Request< 'a >
  {
    /// Original request string.
    pub original : &'a str,
    /// Delimeter for pairs `key:value`.
    pub key_val_delimeter : &'a str,
    /// Delimeter for commands.
    pub commands_delimeter : &'a str,
    /// Parsed subject of first command.
    pub subject : String,
    /// All subjects of the commands in request.
    pub subjects : Vec<String>,
    /// Options map of first command.
    pub map : HashMap<String, OpType<String>>,
    /// All options maps of the commands in request.
    pub maps : Vec<HashMap<String, OpType<String>>>,
  }

  ///
  /// Options for parser.
  ///

  #[ derive( Debug ) ]
  #[ derive( Former ) ]
  #[ perform( fn parse( mut self ) -> Request< 'a > ) ]
  pub struct ParseOptions< 'a >
  {
    #[ default( "" ) ]
    src : &'a str,
    #[ default( ":" ) ]
    key_val_delimeter : &'a str,
    #[ default( ";" ) ]
    commands_delimeter : &'a str,
    #[ default( true ) ]
    quoting : bool,
    #[ default( true ) ]
    unquoting : bool,
    #[ default( true ) ]
    parsing_arrays : bool,
    #[ default( false ) ]
    several_values : bool,
    #[ default( false ) ]
    subject_win_paths_maybe : bool,
  }

  ///
  /// Adapter for ParseOptions.
  ///

  pub trait ParseOptionsAdapter< 'a >
  {
    /// A string to parse.
    fn src( &self ) -> &'a str;
    /// A delimeter for pairs `key:value`.
    fn key_val_delimeter( &self ) -> &'a str;
    /// Delimeter for commands.
    fn commands_delimeter( &self ) -> &'a str;
    /// Quoting of strings.
    fn quoting( &self ) -> bool;
    /// Unquoting of string.
    fn unquoting( &self ) -> bool;
    /// Parse arrays of values.
    fn parsing_arrays( &self ) -> bool;
    /// Append to a vector a values.
    fn several_values( &self ) -> bool;
    /// Parse subject on Windows taking into account colon in path.
    fn subject_win_paths_maybe( &self ) -> bool;

    /// Do parsing.
    fn parse( self ) -> Request< 'a >
    where
      Self : Sized,
    {
      Request::default()
    }
  }

  impl< 'a > ParseOptionsAdapter< 'a > for ParseOptions< 'a >
  {
    fn src( &self ) -> &'a str
    {
      self.src
    }
    fn key_val_delimeter( &self ) -> &'a str
    {
      self.key_val_delimeter
    }
    fn commands_delimeter( &self ) -> &'a str
    {
      self.commands_delimeter
    }
    fn quoting( &self ) -> bool
    {
      self.quoting
    }
    fn unquoting( &self ) -> bool
    {
      self.unquoting
    }
    fn parsing_arrays( &self ) -> bool
    {
      self.parsing_arrays
    }
    fn several_values( &self ) -> bool
    {
      self.several_values
    }
    fn subject_win_paths_maybe( &self ) -> bool
    {
      self.subject_win_paths_maybe
    }

    fn parse( mut self ) -> Request< 'a >
    where
      Self : Sized,
    {
      let mut result = Request::default();

      result.original = self.src();
      result.key_val_delimeter = self.key_val_delimeter();
      result.commands_delimeter = self.commands_delimeter();

      self.src = self.src.trim();

      if self.src.is_empty()
      {
        return result;
      }

      let commands;
      if self.commands_delimeter.trim().is_empty()
      {
        commands = vec![ self.src().to_string() ];
      }
      else
      {
        /* qqq : should use quoting */
        let iter = split()
        .src( self.src() )
        .delimeter( self.commands_delimeter() )
        // .quoting( self.quoting() )
        .stripping( true )
        .preserving_empty( false )
        .preserving_delimeters( false )
        .perform();
        commands = iter.map( | e | String::from( e ) ).collect::< Vec< _ > >();
      }

      for command in commands
      {
        let mut map_entries;
        if self.key_val_delimeter.trim().is_empty()
        {
          map_entries =  ( command.as_str(), None, "" );
        }
        else
        {
          map_entries = match command.split_once( self.key_val_delimeter )
          {
            Some( entries ) => ( entries.0, Some( self.key_val_delimeter ), entries.1 ),
            None => ( command.as_str(), None, "" ),
          };
        }

        let subject;
        let mut map : HashMap<String, OpType<String>> = HashMap::new();

        if map_entries.1.is_some()
        {
          /* qqq : should be isolate_right* with option quoting */
          let subject_and_key = isolate_right_or_none( map_entries.0.trim(), " " );
          subject = subject_and_key.0;
          map_entries.0 = subject_and_key.2;

          let mut join = String::from( map_entries.0 );
          join.push_str( map_entries.1.unwrap() );
          join.push_str( map_entries.2 );

          /* qqq : implement preserving_quoting */
          let mut splits = split()
          .src( join.as_str() )
          .delimeter( self.key_val_delimeter )
          .stripping( false )
          // .quoting( self.quoting )
          .preserving_empty( true )
          .preserving_delimeters( true )
          // .preserving_quoting( true )
          .perform()
          .map( | e | String::from( e ) ).collect::< Vec< _ > >();

          let mut pairs = vec![];
          for a in ( 0..splits.len() - 2 ).step_by( 2 )
          {
            let mut right = splits[ a + 2 ].clone();

            while a < ( splits.len() - 3 )
            {
              let cuts = isolate_right_or_none( &right.trim(), " " );

              if cuts.1.is_none()
              {
                let mut joined = splits[ a + 2 ].clone();
                joined.push_str( splits[ a + 3 ].as_str() );
                joined.push_str( splits[ a + 4 ].as_str() );

                splits[ a + 2 ] = joined;
                right = splits[ a + 2 ].clone();
                splits.remove( a + 3 );
                splits.remove( a + 4 );
                continue;
              }

              splits[ a + 2 ] = cuts.2.to_string();
              right = cuts.0.to_string();
              break;
            }

            let left = splits[ a ].clone();
            let right = right.trim().to_string();
            if self.unquoting
            {
              if left.contains( "\"" ) || left.contains( "'" ) || right.contains( "\"" ) || right.contains( "'" )
              {
                unimplemented!( "not implemented" );
              }
              // left = str_unquote( left );
              // right = str_unquote( right );
            }

            pairs.push( left );
            pairs.push( right );
          }

          /* */

          let str_to_vec_maybe = | src : &str | -> Option<Vec<String>>
          {
            if !src.starts_with( '[' ) || !src.ends_with( ']' )
            {
              return None;
            }

            let splits = split()
            .src( &src[ 1..src.len() - 1 ] )
            .delimeter( "," )
            .stripping( true )
            // .quoting( self.quoting )
            .preserving_empty( false )
            .preserving_delimeters( false )
            // .preserving_quoting( false )
            .perform()
            .map( | e | String::from( e ).trim().to_owned() ).collect::< Vec<String> >();

            Some( splits )
          };

          /* */

          for a in ( 0..pairs.len() - 1 ).step_by( 2 )
          {
            let left = &pairs[ a ];
            let right_str = &pairs[ a + 1 ];
            let mut right = OpType::Primitive( pairs[ a + 1 ].to_string() );

            if self.parsing_arrays
            {
              if let Some( vector ) = str_to_vec_maybe( right_str )
              {
                right = OpType::Vector( vector );
              }
            }

            if self.several_values
            {
              if let Some( op ) = map.get( left )
              {
                let value = op.clone().append( right );
                map.insert( left.to_string(), value );
              }
              else
              {
                map.insert( left.to_string(), right );
              }
            }
            else
            {
              map.insert( left.to_string(), right );
            }
          }
        }
        else
        {
          subject = map_entries.0;
        }

        if self.unquoting
        {
          if subject.contains( "\"" ) || subject.contains( "'" )
          {
            unimplemented!( "not implemented" );
          }
          // subject = _.strUnquote( subject );
        }

        if self.subject_win_paths_maybe
        {
          unimplemented!( "not implemented" );
          // subject = win_path_subject_check( subject, map );
        }

        result.subjects.push( subject.to_string() );
        result.maps.push( map );
      }

      if result.subjects.len() > 0
      {
        result.subject = result.subjects[ 0 ].clone();
      }
      if result.maps.len() > 0
      {
        result.map = result.maps[ 0 ].clone();
      }

      result
    }
  }

  fn isolate_right_or_none<'a>( src : &'a str, delimeter : &'a str ) -> ( &'a str, Option<&'a str>, &'a str )
  {
    let result = match src.trim().rsplit_once( delimeter )
    {
      Some( entries ) => ( entries.0, Some( delimeter ), entries.1 ),
      None => ( "", None, src ),
    };
    result
  }

  ///
  /// Function to parse a string with command request.
  ///
  /// It produces former. To convert former into options and run algorithm of splitting call `perform()`.
  ///

  pub fn request_parse<'a>() -> ParseOptionsFormer<'a>
  {
    ParseOptions::former()
  }
}

/// Protected namespace of the module.
pub mod protected
{
  pub use super::orphan::*;
  pub use super::private::
  {
    OpType,
    Request,
    ParseOptions,
    ParseOptionsAdapter,
    request_parse,
  };
}

#[ doc( inline ) ]
pub use protected::*;

/// Parented namespace of the module.
pub mod orphan
{
  pub use super::exposed::*;
}

/// Exposed namespace of the module.
pub mod exposed
{
  pub use super::private::
  {
    ParseOptionsAdapter,
    request_parse,
  };
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  pub use super::private::ParseOptionsAdapter;
}
<commit_msg>Improve module `parse`, use missed options to parse commands<commit_after>/// Internal namespace.
pub( crate ) mod private
{
  use former::Former;
  use crate::string::split::*;
  use crate::string::isolate::isolate_right;
  use std::collections::HashMap;

  ///
  /// Wrapper types to make transformation.
  ///

  #[ derive( Debug, Clone, PartialEq ) ]
  pub enum OpType<T>
  {
    /// Wrapper over single element of type <T>.
    Primitive( T ),
    /// Wrapper over vector of elements of type <T>.
    Vector( Vec<T> ),
    /// Wrapper over hash map of elements of type <T>.
    Map( HashMap<String, T> ),
  }

  impl<T : Default> Default for OpType<T>
  {
    fn default() -> Self
    {
      OpType::Primitive( T::default() )
    }
  }

  impl<T> From<T> for OpType<T>
  {
    fn from( value: T ) -> Self
    {
      OpType::Primitive( value )
    }
  }

  impl<T> From<Vec<T>> for OpType<T>
  {
    fn from( value: Vec<T> ) -> Self
    {
      OpType::Vector( value )
    }
  }

  impl<T> Into<Vec<T>> for OpType<T>
  {
    fn into( self ) -> Vec<T>
    {
      match self
      {
        OpType::Vector( vec ) => vec,
        _ => unimplemented!( "not implemented" ),
      }
    }
  }

  impl<T : Clone> OpType<T>
  {
    /// Append item of OpType to current value. If current type is `Primitive`, then it will be converted to
    /// `Vector`.
    pub fn append( mut self, item : OpType<T> ) -> OpType<T>
    {
      let mut mut_item = item;
      match self
      {
        OpType::Primitive( value ) =>
        {
          match mut_item
          {
            OpType::Primitive( ins ) =>
            {
              let vector = vec![ value, ins ];
              OpType::Vector( vector )
            }
            OpType::Vector( ref mut vector ) =>
            {
              vector.insert( 0, value );
              mut_item
            },
            OpType::Map( _ ) => panic!( "Unexpected operation. Please, use method `insert` to insert item in hash map." ),
          }
        },
        OpType::Vector( ref mut vector ) =>
        {
          match mut_item
          {
            OpType::Primitive( ins ) =>
            {
              vector.push( ins );
              self
            }
            OpType::Vector( ref mut ins_vec ) =>
            {
              vector.append( ins_vec );
              self
            },
            OpType::Map( _ ) => panic!( "Unexpected operation. Please, use method `insert` to insert item in hash map." ),
          }
        },
        OpType::Map( _ ) => panic!( "Unexpected operation. Please, use method `insert` to insert item in hash map." ),
      }
    }

    /// Unwrap primitive value. Consumes self.
    pub fn primitive( self ) -> Option<T>
    {
      match self
      {
        OpType::Primitive( v ) => Some( v ),
        _ => None,
      }
    }

    /// Unwrap vector value. Consumes self.
    pub fn vector( self ) -> Option<Vec<T>>
    {
      match self
      {
        OpType::Vector( vec ) => Some( vec ),
        _ => None,
      }
    }
  }

  ///
  /// Parsed request data.
  ///

  #[ allow( dead_code ) ]
  #[ derive( Debug, Default, PartialEq ) ]
  pub struct Request< 'a >
  {
    /// Original request string.
    pub original : &'a str,
    /// Delimeter for pairs `key:value`.
    pub key_val_delimeter : &'a str,
    /// Delimeter for commands.
    pub commands_delimeter : &'a str,
    /// Parsed subject of first command.
    pub subject : String,
    /// All subjects of the commands in request.
    pub subjects : Vec<String>,
    /// Options map of first command.
    pub map : HashMap<String, OpType<String>>,
    /// All options maps of the commands in request.
    pub maps : Vec<HashMap<String, OpType<String>>>,
  }

  ///
  /// Options for parser.
  ///

  #[ derive( Debug ) ]
  #[ derive( Former ) ]
  #[ perform( fn parse( mut self ) -> Request< 'a > ) ]
  pub struct ParseOptions< 'a >
  {
    #[ default( "" ) ]
    src : &'a str,
    #[ default( ":" ) ]
    key_val_delimeter : &'a str,
    #[ default( ";" ) ]
    commands_delimeter : &'a str,
    #[ default( true ) ]
    quoting : bool,
    #[ default( true ) ]
    unquoting : bool,
    #[ default( true ) ]
    parsing_arrays : bool,
    #[ default( false ) ]
    several_values : bool,
    #[ default( false ) ]
    subject_win_paths_maybe : bool,
  }

  ///
  /// Adapter for ParseOptions.
  ///

  pub trait ParseOptionsAdapter< 'a >
  {
    /// A string to parse.
    fn src( &self ) -> &'a str;
    /// A delimeter for pairs `key:value`.
    fn key_val_delimeter( &self ) -> &'a str;
    /// Delimeter for commands.
    fn commands_delimeter( &self ) -> &'a str;
    /// Quoting of strings.
    fn quoting( &self ) -> bool;
    /// Unquoting of string.
    fn unquoting( &self ) -> bool;
    /// Parse arrays of values.
    fn parsing_arrays( &self ) -> bool;
    /// Append to a vector a values.
    fn several_values( &self ) -> bool;
    /// Parse subject on Windows taking into account colon in path.
    fn subject_win_paths_maybe( &self ) -> bool;

    /// Do parsing.
    fn parse( self ) -> Request< 'a >
    where
      Self : Sized,
    {
      Request::default()
    }
  }

  impl< 'a > ParseOptionsAdapter< 'a > for ParseOptions< 'a >
  {
    fn src( &self ) -> &'a str
    {
      self.src
    }
    fn key_val_delimeter( &self ) -> &'a str
    {
      self.key_val_delimeter
    }
    fn commands_delimeter( &self ) -> &'a str
    {
      self.commands_delimeter
    }
    fn quoting( &self ) -> bool
    {
      self.quoting
    }
    fn unquoting( &self ) -> bool
    {
      self.unquoting
    }
    fn parsing_arrays( &self ) -> bool
    {
      self.parsing_arrays
    }
    fn several_values( &self ) -> bool
    {
      self.several_values
    }
    fn subject_win_paths_maybe( &self ) -> bool
    {
      self.subject_win_paths_maybe
    }

    fn parse( mut self ) -> Request< 'a >
    where
      Self : Sized,
    {
      let mut result = Request::default();

      result.original = self.src();
      result.key_val_delimeter = self.key_val_delimeter();
      result.commands_delimeter = self.commands_delimeter();

      self.src = self.src.trim();

      if self.src.is_empty()
      {
        return result;
      }

      let commands;
      if self.commands_delimeter.trim().is_empty()
      {
        commands = vec![ self.src().to_string() ];
      }
      else
      {
        let iter = split()
        .src( self.src() )
        .delimeter( self.commands_delimeter() )
        .quoting( self.quoting() )
        .stripping( true )
        .preserving_empty( false )
        .preserving_delimeters( false )
        .perform();
        commands = iter.map( | e | String::from( e ) ).collect::< Vec< _ > >();
      }

      for command in commands
      {
        let mut map_entries;
        if self.key_val_delimeter.trim().is_empty()
        {
          map_entries =  ( command.as_str(), None, "" );
        }
        else
        {
          map_entries = match command.split_once( self.key_val_delimeter )
          {
            Some( entries ) => ( entries.0, Some( self.key_val_delimeter ), entries.1 ),
            None => ( command.as_str(), None, "" ),
          };
        }

        let subject;
        let mut map : HashMap<String, OpType<String>> = HashMap::new();

        if map_entries.1.is_some()
        {
          let subject_and_key = isolate_right()
          .src( map_entries.0.trim() )
          .delimeter( " " )
          .none( true )
          .perform();
          subject = subject_and_key.0;
          map_entries.0 = subject_and_key.2;

          let mut join = String::from( map_entries.0 );
          join.push_str( map_entries.1.unwrap() );
          join.push_str( map_entries.2 );

          let mut splits = split()
          .src( join.as_str() )
          .delimeter( self.key_val_delimeter )
          .stripping( false )
          .quoting( self.quoting )
          .preserving_empty( true )
          .preserving_delimeters( true )
          .preserving_quoting( true )
          .perform()
          .map( | e | String::from( e ) ).collect::< Vec< _ > >();

          let mut pairs = vec![];
          for a in ( 0..splits.len() - 2 ).step_by( 2 )
          {
            let mut right = splits[ a + 2 ].clone();

            while a < ( splits.len() - 3 )
            {
              let cuts = isolate_right()
              .src( right.trim() )
              .delimeter( " " )
              .none( true )
              .perform();

              if cuts.1.is_none()
              {
                let mut joined = splits[ a + 2 ].clone();
                joined.push_str( splits[ a + 3 ].as_str() );
                joined.push_str( splits[ a + 4 ].as_str() );

                splits[ a + 2 ] = joined;
                right = splits[ a + 2 ].clone();
                splits.remove( a + 3 );
                splits.remove( a + 4 );
                continue;
              }

              splits[ a + 2 ] = cuts.2.to_string();
              right = cuts.0.to_string();
              break;
            }

            let left = splits[ a ].clone();
            let right = right.trim().to_string();
            if self.unquoting
            {
              if left.contains( "\"" ) || left.contains( "'" ) || right.contains( "\"" ) || right.contains( "'" )
              {
                unimplemented!( "not implemented" );
              }
              // left = str_unquote( left );
              // right = str_unquote( right );
            }

            pairs.push( left );
            pairs.push( right );
          }

          /* */

          let str_to_vec_maybe = | src : &str | -> Option<Vec<String>>
          {
            if !src.starts_with( '[' ) || !src.ends_with( ']' )
            {
              return None;
            }

            let splits = split()
            .src( &src[ 1..src.len() - 1 ] )
            .delimeter( "," )
            .stripping( true )
            .quoting( self.quoting )
            .preserving_empty( false )
            .preserving_delimeters( false )
            .preserving_quoting( false )
            .perform()
            .map( | e | String::from( e ).trim().to_owned() ).collect::< Vec<String> >();

            Some( splits )
          };

          /* */

          for a in ( 0..pairs.len() - 1 ).step_by( 2 )
          {
            let left = &pairs[ a ];
            let right_str = &pairs[ a + 1 ];
            let mut right = OpType::Primitive( pairs[ a + 1 ].to_string() );

            if self.parsing_arrays
            {
              if let Some( vector ) = str_to_vec_maybe( right_str )
              {
                right = OpType::Vector( vector );
              }
            }

            if self.several_values
            {
              if let Some( op ) = map.get( left )
              {
                let value = op.clone().append( right );
                map.insert( left.to_string(), value );
              }
              else
              {
                map.insert( left.to_string(), right );
              }
            }
            else
            {
              map.insert( left.to_string(), right );
            }
          }
        }
        else
        {
          subject = map_entries.0;
        }

        if self.unquoting
        {
          if subject.contains( "\"" ) || subject.contains( "'" )
          {
            unimplemented!( "not implemented" );
          }
          // subject = _.strUnquote( subject );
        }

        if self.subject_win_paths_maybe
        {
          unimplemented!( "not implemented" );
          // subject = win_path_subject_check( subject, map );
        }

        result.subjects.push( subject.to_string() );
        result.maps.push( map );
      }

      if result.subjects.len() > 0
      {
        result.subject = result.subjects[ 0 ].clone();
      }
      if result.maps.len() > 0
      {
        result.map = result.maps[ 0 ].clone();
      }

      result
    }
  }

  ///
  /// Function to parse a string with command request.
  ///
  /// It produces former. To convert former into options and run algorithm of splitting call `perform()`.
  ///

  pub fn request_parse<'a>() -> ParseOptionsFormer<'a>
  {
    ParseOptions::former()
  }
}

/// Protected namespace of the module.
pub mod protected
{
  pub use super::orphan::*;
  pub use super::private::
  {
    OpType,
    Request,
    ParseOptions,
    ParseOptionsAdapter,
    request_parse,
  };
}

#[ doc( inline ) ]
pub use protected::*;

/// Parented namespace of the module.
pub mod orphan
{
  pub use super::exposed::*;
}

/// Exposed namespace of the module.
pub mod exposed
{
  pub use super::private::
  {
    ParseOptionsAdapter,
    request_parse,
  };
}

/// Namespace of the module to include with `use module::*`.
pub mod prelude
{
  pub use super::private::ParseOptionsAdapter;
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-fast Feature gating doesn't work
// ignore-stage1
// ignore-pretty
// ignore-android

#[feature(phase)];

#[phase(syntax)]
extern mod fourcc;

static static_val: u32 = fourcc!("foo ");
static static_val_be: u32 = fourcc!("foo ", big);
static static_val_le: u32 = fourcc!("foo ", little);
static static_val_target: u32 = fourcc!("foo ", target);

fn main() {
    let val = fourcc!("foo ", big);
    assert_eq!(val, 0x666f6f20u32);
    assert_eq!(val, fourcc!("foo "));

    let val = fourcc!("foo ", little);
    assert_eq!(val, 0x206f6f66u32);

    let val = fourcc!("foo ", target);
    let exp = if cfg!(target_endian = "big") { 0x666f6f20u32 } else { 0x206f6f66u32 };
    assert_eq!(val, exp);

    assert_eq!(static_val_be, 0x666f6f20u32);
    assert_eq!(static_val, static_val_be);
    assert_eq!(static_val_le, 0x206f6f66u32);
    let exp = if cfg!(target_endian = "big") { 0x666f6f20u32 } else { 0x206f6f66u32 };
    assert_eq!(static_val_target, exp);

    assert_eq!(fourcc!("\xC0\xFF\xEE!"), 0xC0FFEE21);
}
<commit_msg>Ignore another fourcc test on cross compiles<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-fast Feature gating doesn't work
// ignore-stage1
// ignore-pretty
// ignore-cross-compile

#[feature(phase)];

#[phase(syntax)]
extern mod fourcc;

static static_val: u32 = fourcc!("foo ");
static static_val_be: u32 = fourcc!("foo ", big);
static static_val_le: u32 = fourcc!("foo ", little);
static static_val_target: u32 = fourcc!("foo ", target);

fn main() {
    let val = fourcc!("foo ", big);
    assert_eq!(val, 0x666f6f20u32);
    assert_eq!(val, fourcc!("foo "));

    let val = fourcc!("foo ", little);
    assert_eq!(val, 0x206f6f66u32);

    let val = fourcc!("foo ", target);
    let exp = if cfg!(target_endian = "big") { 0x666f6f20u32 } else { 0x206f6f66u32 };
    assert_eq!(val, exp);

    assert_eq!(static_val_be, 0x666f6f20u32);
    assert_eq!(static_val, static_val_be);
    assert_eq!(static_val_le, 0x206f6f66u32);
    let exp = if cfg!(target_endian = "big") { 0x666f6f20u32 } else { 0x206f6f66u32 };
    assert_eq!(static_val_target, exp);

    assert_eq!(fourcc!("\xC0\xFF\xEE!"), 0xC0FFEE21);
}
<|endoftext|>


<commit_before>// cargo bench

#![feature(test)]

extern crate test;

macro_rules! benches {
    ($($name:ident($value:expr),)*) => {
        mod bench_ryu {
            use test::{Bencher, black_box};
            $(
                #[bench]
                fn $name(b: &mut Bencher) {
                    use ryu;

                    let mut buf = ryu::Buffer::new();

                    b.iter(move || {
                        let value = black_box($value);
                        let formatted = buf.format_finite(value);
                        black_box(formatted);
                    });
                }
            )*
        }

        mod bench_std_fmt {
            use test::{Bencher, black_box};
            $(
                #[bench]
                fn $name(b: &mut Bencher) {
                    use std::io::Write;

                    let mut buf = Vec::with_capacity(20);

                    b.iter(|| {
                        buf.clear();
                        let value = black_box($value);
                        write!(&mut buf, "{}", value).unwrap();
                        black_box(buf.as_slice());
                    });
                }
            )*
        }
    }
}

benches!(
    bench_0_f64(0f64),
    bench_short_f64(0.1234f64),
    bench_e_f64(2.718281828459045f64),
    bench_max_f64(::std::f64::MAX),
    bench_0_f32(0f32),
    bench_short_f32(0.1234f32),
    bench_e_f32(2.718281828459045f32),
    bench_max_f32(::std::f32::MAX),
);
<commit_msg>Remove obsolete crate use from benchmark<commit_after>// cargo bench

#![feature(test)]

extern crate test;

macro_rules! benches {
    ($($name:ident($value:expr),)*) => {
        mod bench_ryu {
            use test::{Bencher, black_box};
            $(
                #[bench]
                fn $name(b: &mut Bencher) {
                    let mut buf = ryu::Buffer::new();

                    b.iter(move || {
                        let value = black_box($value);
                        let formatted = buf.format_finite(value);
                        black_box(formatted);
                    });
                }
            )*
        }

        mod bench_std_fmt {
            use test::{Bencher, black_box};
            $(
                #[bench]
                fn $name(b: &mut Bencher) {
                    use std::io::Write;

                    let mut buf = Vec::with_capacity(20);

                    b.iter(|| {
                        buf.clear();
                        let value = black_box($value);
                        write!(&mut buf, "{}", value).unwrap();
                        black_box(buf.as_slice());
                    });
                }
            )*
        }
    }
}

benches!(
    bench_0_f64(0f64),
    bench_short_f64(0.1234f64),
    bench_e_f64(2.718281828459045f64),
    bench_max_f64(::std::f64::MAX),
    bench_0_f32(0f32),
    bench_short_f32(0.1234f32),
    bench_e_f32(2.718281828459045f32),
    bench_max_f32(::std::f32::MAX),
);
<|endoftext|>


<commit_before>//! See `complete_fn_param`.

use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, ModuleItemOwner},
    match_ast, AstNode,
};

use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};

/// Complete repeated parameters, both name and type. For example, if all
/// functions in a file have a `spam: &mut Spam` parameter, a completion with
/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
/// suggested.
pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
    if !ctx.is_param {
        return;
    }

    let mut params = FxHashMap::default();

    let me = ctx.token.ancestors().find_map(ast::Fn::cast);
    let mut process_fn = |func: ast::Fn| {
        if Some(&func) == me.as_ref() {
            return;
        }
        func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
            if let Some(pat) = param.pat() {
                let text = param.syntax().text().to_string();
                let lookup = pat.syntax().text().to_string();
                params.entry(text).or_insert(lookup);
            }
        });
    };

    for node in ctx.token.ancestors() {
        match_ast! {
            match node {
                ast::SourceFile(it) => it.items().filter_map(|item| match item {
                    ast::Item::Fn(it) => Some(it),
                    _ => None,
                }).for_each(&mut process_fn),
                ast::ItemList(it) => it.items().filter_map(|item| match item {
                    ast::Item::Fn(it) => Some(it),
                    _ => None,
                }).for_each(&mut process_fn),
                ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
                    ast::AssocItem::Fn(it) => Some(it),
                    _ => None,
                }).for_each(&mut process_fn),
                _ => continue,
            }
        };
    }

    params.into_iter().for_each(|(label, lookup)| {
        let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
        item.kind(CompletionItemKind::Binding).lookup_by(lookup);
        item.add_to(acc)
    });
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::{tests::filtered_completion_list, CompletionKind};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
        expect.assert_eq(&actual);
    }

    #[test]
    fn test_param_completion_last_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn bar(file_id: FileId) {}
fn baz(file$0) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_nth_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn baz(file$0, x: i32) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_trait_param() {
        check(
            r#"
pub(crate) trait SourceRoot {
    pub fn contains(&self, file_id: FileId) -> bool;
    pub fn module_map(&self) -> &ModuleMap;
    pub fn lines(&self, file_id: FileId) -> &LineIndex;
    pub fn syntax(&self, file$0)
}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn completes_param_in_inner_function() {
        check(
            r#"
fn outer(text: String) {
    fn inner($0)
}
"#,
            expect![[r#"
                bn text: String
            "#]],
        )
    }

    #[test]
    fn completes_non_ident_pat_param() {
        check(
            r#"
struct Bar { bar: u32 }

fn foo(Bar { bar }: Bar) {}
fn foo2($0) {}
"#,
            expect![[r#"
                bn Bar { bar }: Bar
            "#]],
        )
    }
}
<commit_msg>add: Adding self keyword completion in complete_fn_param<commit_after>//! See `complete_fn_param`.

use rustc_hash::FxHashMap;
use syntax::{
    ast::{self, ModuleItemOwner},
    match_ast, AstNode,
};

use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions};

/// Complete repeated parameters, both name and type. For example, if all
/// functions in a file have a `spam: &mut Spam` parameter, a completion with
/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
/// suggested.
pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    if !ctx.is_param {
        return None;
    }

    let mut params = FxHashMap::default();

    let me = ctx.token.ancestors().find_map(ast::Fn::cast);
    let mut process_fn = |func: ast::Fn| {
        if Some(&func) == me.as_ref() {
            return;
        }
        func.param_list().into_iter().flat_map(|it| it.params()).for_each(|param| {
            if let Some(pat) = param.pat() {
                let text = param.syntax().text().to_string();
                let lookup = pat.syntax().text().to_string();
                params.entry(text).or_insert(lookup);
            }
        });
    };

    for node in ctx.token.ancestors() {
        match_ast! {
            match node {
                ast::SourceFile(it) => it.items().filter_map(|item| match item {
                    ast::Item::Fn(it) => Some(it),
                    _ => None,
                }).for_each(&mut process_fn),
                ast::ItemList(it) => it.items().filter_map(|item| match item {
                    ast::Item::Fn(it) => Some(it),
                    _ => None,
                }).for_each(&mut process_fn),
                ast::AssocItemList(it) => it.assoc_items().filter_map(|item| match item {
                    ast::AssocItem::Fn(it) => Some(it),
                    _ => None,
                }).for_each(&mut process_fn),
                _ => continue,
            }
        };
    }

    let self_completion_items = ["self", "&self", "mut self", "&mut self"];
    if ctx.impl_def.is_some() && me?.param_list()?.params().next().is_none() {
        self_completion_items.iter().for_each(|self_item| {
            add_new_item_to_acc(ctx, acc, self_item.to_string(), self_item.to_string())
        });
    }

    params.into_iter().for_each(|(label, lookup)| add_new_item_to_acc(ctx, acc, label, lookup));

    Some(())
}

fn add_new_item_to_acc(
    ctx: &CompletionContext,
    acc: &mut Completions,
    label: String,
    lookup: String,
) {
    let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
    item.kind(CompletionItemKind::Binding).lookup_by(lookup);
    item.add_to(acc)
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::{tests::filtered_completion_list, CompletionKind};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = filtered_completion_list(ra_fixture, CompletionKind::Magic);
        expect.assert_eq(&actual);
    }

    #[test]
    fn test_param_completion_last_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn bar(file_id: FileId) {}
fn baz(file$0) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_nth_param() {
        check(
            r#"
fn foo(file_id: FileId) {}
fn baz(file$0, x: i32) {}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn test_param_completion_trait_param() {
        check(
            r#"
pub(crate) trait SourceRoot {
    pub fn contains(&self, file_id: FileId) -> bool;
    pub fn module_map(&self) -> &ModuleMap;
    pub fn lines(&self, file_id: FileId) -> &LineIndex;
    pub fn syntax(&self, file$0)
}
"#,
            expect![[r#"
                bn file_id: FileId
            "#]],
        );
    }

    #[test]
    fn completes_param_in_inner_function() {
        check(
            r#"
fn outer(text: String) {
    fn inner($0)
}
"#,
            expect![[r#"
                bn text: String
            "#]],
        )
    }

    #[test]
    fn completes_non_ident_pat_param() {
        check(
            r#"
struct Bar { bar: u32 }

fn foo(Bar { bar }: Bar) {}
fn foo2($0) {}
"#,
            expect![[r#"
                bn Bar { bar }: Bar
            "#]],
        )
    }

    #[test]
    fn test_param_completion_self_param() {
        check(
            r#"
                struct A {}

                impl A {
                    fn foo(file_id: FileId) {}
                    fn new($0) {
                    }
                }
            "#,
            expect![[r#"
                bn self
                bn &self
                bn mut self
                bn &mut self
                bn file_id: FileId
            "#]],
        )
    }
}
<|endoftext|>