mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-27 21:33:09 +00:00
Reimplement environment and the environment stack in Rust
This reimplements the environment stack in Rust.
This commit is contained in:
parent
8ec1467dda
commit
e71b75e0e4
4 changed files with 355 additions and 1171 deletions
256
fish-rust/src/env/env_ffi.rs
vendored
256
fish-rust/src/env/env_ffi.rs
vendored
|
@ -1,8 +1,13 @@
|
|||
use super::var::{EnvVar, EnvVarFlags};
|
||||
use crate::ffi::{wchar_t, wcharz_t, wcstring_list_ffi_t};
|
||||
use super::environment::{self, EnvNull, EnvStack, EnvStackRef, Environment};
|
||||
use super::var::{ElectricVar, EnvVar, EnvVarFlags, Statuses};
|
||||
use crate::env::EnvMode;
|
||||
use crate::event::Event;
|
||||
use crate::ffi::{event_list_ffi_t, wchar_t, wcharz_t, wcstring_list_ffi_t};
|
||||
use crate::null_terminated_array::OwningNullTerminatedArrayRefFFI;
|
||||
use crate::signal::Signal;
|
||||
use crate::wchar_ffi::WCharToFFI;
|
||||
use crate::wchar_ffi::{AsWstr, WCharFromFFI};
|
||||
use cxx::{CxxWString, UniquePtr};
|
||||
use cxx::{CxxVector, CxxWString, UniquePtr};
|
||||
use std::pin::Pin;
|
||||
|
||||
#[allow(clippy::module_inception)]
|
||||
|
@ -20,9 +25,15 @@ mod env_ffi {
|
|||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("env.h");
|
||||
include!("null_terminated_array.h");
|
||||
include!("wutil.h");
|
||||
type event_list_ffi_t = super::event_list_ffi_t;
|
||||
type wcstring_list_ffi_t = super::wcstring_list_ffi_t;
|
||||
type wcharz_t = super::wcharz_t;
|
||||
|
||||
type OwningNullTerminatedArrayRefFFI =
|
||||
crate::null_terminated_array::OwningNullTerminatedArrayRefFFI;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
|
@ -64,6 +75,81 @@ mod env_ffi {
|
|||
values: &wcstring_list_ffi_t,
|
||||
) -> Box<EnvVar>;
|
||||
}
|
||||
extern "Rust" {
|
||||
type EnvNull;
|
||||
#[cxx_name = "getf"]
|
||||
fn getf_ffi(&self, name: &CxxWString, mode: u16) -> *mut EnvVar;
|
||||
#[cxx_name = "get_names"]
|
||||
fn get_names_ffi(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>);
|
||||
#[cxx_name = "env_null_create"]
|
||||
fn env_null_create_ffi() -> Box<EnvNull>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type Statuses;
|
||||
#[cxx_name = "get_status"]
|
||||
fn get_status_ffi(&self) -> i32;
|
||||
|
||||
#[cxx_name = "get_pipestatus"]
|
||||
fn get_pipestatus_ffi(&self) -> &Vec<i32>;
|
||||
|
||||
#[cxx_name = "get_kill_signal"]
|
||||
fn get_kill_signal_ffi(&self) -> i32;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
#[cxx_name = "EnvDyn"]
|
||||
type EnvDynFFI;
|
||||
fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar;
|
||||
fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>);
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
#[cxx_name = "EnvStackRef"]
|
||||
type EnvStackRefFFI;
|
||||
fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar;
|
||||
fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>);
|
||||
fn is_principal(&self) -> bool;
|
||||
fn get_last_statuses(&self) -> Box<Statuses>;
|
||||
fn set_last_statuses(&self, status: i32, kill_signal: i32, pipestatus: &CxxVector<i32>);
|
||||
fn set(
|
||||
&self,
|
||||
name: &CxxWString,
|
||||
flags: u16,
|
||||
vals: &wcstring_list_ffi_t,
|
||||
) -> EnvStackSetResult;
|
||||
fn remove(&self, name: &CxxWString, flags: u16) -> EnvStackSetResult;
|
||||
fn get_pwd_slash(&self) -> UniquePtr<CxxWString>;
|
||||
fn set_pwd_from_getcwd(&self);
|
||||
|
||||
fn push(&mut self, new_scope: bool);
|
||||
fn pop(&mut self);
|
||||
|
||||
// Returns a ``Box<OwningNullTerminatedArrayRefFFI>.into_raw()``.
|
||||
fn export_array(&self) -> *mut OwningNullTerminatedArrayRefFFI;
|
||||
|
||||
fn snapshot(&self) -> Box<EnvDynFFI>;
|
||||
|
||||
// Access a variable stack that only represents globals.
|
||||
// Do not push or pop from this.
|
||||
fn env_get_globals_ffi() -> Box<EnvStackRefFFI>;
|
||||
|
||||
// Access the principal variable stack.
|
||||
fn env_get_principal_ffi() -> Box<EnvStackRefFFI>;
|
||||
|
||||
fn universal_sync(&self, always: bool, out_events: Pin<&mut event_list_ffi_t>);
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
#[cxx_name = "var_is_electric"]
|
||||
fn var_is_electric_ffi(name: &CxxWString) -> bool;
|
||||
|
||||
#[cxx_name = "rust_env_init"]
|
||||
fn rust_env_init_ffi(do_uvars: bool);
|
||||
|
||||
#[cxx_name = "env_flags_for"]
|
||||
fn env_flags_for_ffi(name: wcharz_t) -> u8;
|
||||
}
|
||||
}
|
||||
pub use env_ffi::EnvStackSetResult;
|
||||
|
||||
|
@ -117,3 +203,167 @@ fn env_var_create_ffi(vals: &wcstring_list_ffi_t, flags: u8) -> Box<EnvVar> {
|
|||
pub fn env_var_create_from_name_ffi(name: wcharz_t, values: &wcstring_list_ffi_t) -> Box<EnvVar> {
|
||||
Box::new(EnvVar::new_from_name_vec(name.as_wstr(), values.from_ffi()))
|
||||
}
|
||||
|
||||
fn env_null_create_ffi() -> Box<EnvNull> {
|
||||
Box::new(EnvNull::new())
|
||||
}
|
||||
|
||||
/// FFI wrapper around dyn Environment.
|
||||
pub struct EnvDynFFI(Box<dyn Environment>);
|
||||
impl EnvDynFFI {
|
||||
fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar {
|
||||
EnvironmentFFI::getf_ffi(&*self.0, name, mode)
|
||||
}
|
||||
fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>) {
|
||||
EnvironmentFFI::get_names_ffi(&*self.0, flags, out)
|
||||
}
|
||||
}
|
||||
|
||||
/// FFI wrapper around EnvStackRef.
|
||||
pub struct EnvStackRefFFI(EnvStackRef);
|
||||
|
||||
impl EnvStackRefFFI {
|
||||
fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar {
|
||||
EnvironmentFFI::getf_ffi(&*self.0, name, mode)
|
||||
}
|
||||
fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>) {
|
||||
EnvironmentFFI::get_names_ffi(&*self.0, flags, out)
|
||||
}
|
||||
fn is_principal(&self) -> bool {
|
||||
self.0.is_principal()
|
||||
}
|
||||
|
||||
fn get_pwd_slash(&self) -> UniquePtr<CxxWString> {
|
||||
self.0.get_pwd_slash().to_ffi()
|
||||
}
|
||||
|
||||
fn push(&self, new_scope: bool) {
|
||||
self.0.push(new_scope)
|
||||
}
|
||||
|
||||
fn pop(&self) {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
fn get_last_statuses(&self) -> Box<Statuses> {
|
||||
Box::new(self.0.get_last_statuses())
|
||||
}
|
||||
|
||||
fn set_last_statuses(&self, status: i32, kill_signal: i32, pipestatus: &CxxVector<i32>) {
|
||||
let statuses = Statuses {
|
||||
status,
|
||||
kill_signal: if kill_signal == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(Signal::new(kill_signal))
|
||||
},
|
||||
pipestatus: pipestatus.as_slice().to_vec(),
|
||||
};
|
||||
self.0.set_last_statuses(statuses)
|
||||
}
|
||||
|
||||
fn set_pwd_from_getcwd(&self) {
|
||||
self.0.set_pwd_from_getcwd()
|
||||
}
|
||||
|
||||
fn set(&self, name: &CxxWString, flags: u16, vals: &wcstring_list_ffi_t) -> EnvStackSetResult {
|
||||
let mode = EnvMode::from_bits(flags).expect("Invalid mode bits");
|
||||
self.0.set(name.as_wstr(), mode, vals.from_ffi())
|
||||
}
|
||||
|
||||
fn remove(&self, name: &CxxWString, flags: u16) -> EnvStackSetResult {
|
||||
let mode = EnvMode::from_bits(flags).expect("Invalid mode bits");
|
||||
self.0.remove(name.as_wstr(), mode)
|
||||
}
|
||||
|
||||
fn export_array(&self) -> *mut OwningNullTerminatedArrayRefFFI {
|
||||
Box::into_raw(Box::new(OwningNullTerminatedArrayRefFFI(
|
||||
self.0.export_array(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn snapshot(&self) -> Box<EnvDynFFI> {
|
||||
Box::new(EnvDynFFI(self.0.snapshot()))
|
||||
}
|
||||
|
||||
fn universal_sync(
|
||||
self: &EnvStackRefFFI,
|
||||
always: bool,
|
||||
mut out_events: Pin<&mut event_list_ffi_t>,
|
||||
) {
|
||||
let events: Vec<Box<Event>> = self.0.universal_sync(always);
|
||||
for event in events {
|
||||
out_events.as_mut().push(Box::into_raw(event).cast());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Statuses {
|
||||
fn get_status_ffi(&self) -> i32 {
|
||||
self.status
|
||||
}
|
||||
|
||||
fn get_pipestatus_ffi(&self) -> &Vec<i32> {
|
||||
&self.pipestatus
|
||||
}
|
||||
|
||||
fn get_kill_signal_ffi(&self) -> i32 {
|
||||
match self.kill_signal {
|
||||
Some(sig) => sig.code(),
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn env_get_globals_ffi() -> Box<EnvStackRefFFI> {
|
||||
Box::new(EnvStackRefFFI(EnvStack::globals().clone()))
|
||||
}
|
||||
|
||||
fn env_get_principal_ffi() -> Box<EnvStackRefFFI> {
|
||||
Box::new(EnvStackRefFFI(EnvStack::principal().clone()))
|
||||
}
|
||||
|
||||
// We have to implement these directly to make cxx happy, even though they're implemented in the EnvironmentFFI trait.
|
||||
impl EnvNull {
|
||||
pub fn getf_ffi(&self, name: &CxxWString, mode: u16) -> *mut EnvVar {
|
||||
EnvironmentFFI::getf_ffi(self, name, mode)
|
||||
}
|
||||
pub fn get_names_ffi(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>) {
|
||||
EnvironmentFFI::get_names_ffi(self, flags, out)
|
||||
}
|
||||
}
|
||||
|
||||
trait EnvironmentFFI: Environment {
|
||||
/// FFI helper.
|
||||
/// This returns either null, or the result of Box.into_raw().
|
||||
/// This is a workaround for the difficulty of passing an Option through FFI.
|
||||
fn getf_ffi(&self, name: &CxxWString, mode: u16) -> *mut EnvVar {
|
||||
match self.getf(
|
||||
name.as_wstr(),
|
||||
EnvMode::from_bits(mode).expect("Invalid mode bits"),
|
||||
) {
|
||||
None => std::ptr::null_mut(),
|
||||
Some(var) => Box::into_raw(Box::new(var)),
|
||||
}
|
||||
}
|
||||
fn get_names_ffi(&self, mode: u16, mut out: Pin<&mut wcstring_list_ffi_t>) {
|
||||
let names = self.get_names(EnvMode::from_bits(mode).expect("Invalid mode bits"));
|
||||
for name in names {
|
||||
out.as_mut().push(name.to_ffi());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Environment + ?Sized> EnvironmentFFI for T {}
|
||||
|
||||
fn var_is_electric_ffi(name: &CxxWString) -> bool {
|
||||
ElectricVar::for_name(name.as_wstr()).is_some()
|
||||
}
|
||||
|
||||
fn rust_env_init_ffi(do_uvars: bool) {
|
||||
environment::env_init(do_uvars);
|
||||
}
|
||||
|
||||
fn env_flags_for_ffi(name: wcharz_t) -> u8 {
|
||||
EnvVar::flags_for(name.as_wstr()).bits()
|
||||
}
|
||||
|
|
1191
src/env.cpp
1191
src/env.cpp
File diff suppressed because it is too large
Load diff
72
src/env.h
72
src/env.h
|
@ -17,10 +17,14 @@
|
|||
#include "maybe.h"
|
||||
#include "wutil.h"
|
||||
|
||||
struct event_list_ffi_t;
|
||||
|
||||
#if INCLUDE_RUST_HEADERS
|
||||
#include "env/env_ffi.rs.h"
|
||||
#else
|
||||
struct EnvVar;
|
||||
struct EnvNull;
|
||||
struct EnvStackRef;
|
||||
#endif
|
||||
|
||||
/// FFI helper for events.
|
||||
|
@ -42,8 +46,6 @@ struct owning_null_terminated_array_t;
|
|||
extern size_t read_byte_limit;
|
||||
extern bool curses_initialized;
|
||||
|
||||
struct Event;
|
||||
|
||||
// Flags that may be passed as the 'mode' in env_stack_t::set() / environment_t::get().
|
||||
enum : uint16_t {
|
||||
/// Default mode. Used with `env_stack_t::get()` to indicate the caller doesn't care what scope
|
||||
|
@ -114,32 +116,23 @@ void env_init(const struct config_paths_t *paths = nullptr, bool do_uvars = true
|
|||
void misc_init();
|
||||
|
||||
/// env_var_t is an immutable value-type data structure representing the value of an environment
|
||||
/// variable.
|
||||
/// variable. This wraps the EnvVar type from Rust.
|
||||
class env_var_t {
|
||||
public:
|
||||
using env_var_flags_t = uint8_t;
|
||||
|
||||
public:
|
||||
enum {
|
||||
flag_export = 1 << 0, // whether the variable is exported
|
||||
flag_read_only = 1 << 1, // whether the variable is read only
|
||||
flag_pathvar = 1 << 2, // whether the variable is a path variable
|
||||
};
|
||||
|
||||
// Constructors.
|
||||
env_var_t() : env_var_t{std::vector<wcstring>{}, 0} {}
|
||||
env_var_t() : env_var_t(wcstring_list_ffi_t{}, 0) {}
|
||||
env_var_t(const wcstring_list_ffi_t &vals, uint8_t flags);
|
||||
env_var_t(const env_var_t &);
|
||||
env_var_t(env_var_t &&) = default;
|
||||
|
||||
env_var_t(std::vector<wcstring> vals, env_var_flags_t flags);
|
||||
env_var_t(wcstring val, env_var_flags_t flags)
|
||||
: env_var_t{std::vector<wcstring>{std::move(val)}, flags} {}
|
||||
|
||||
// Constructors that infer the flags from a name.
|
||||
env_var_t(const wchar_t *name, std::vector<wcstring> vals);
|
||||
env_var_t(const wchar_t *name, wcstring val)
|
||||
: env_var_t{name, std::vector<wcstring>{std::move(val)}} {}
|
||||
|
||||
// Construct from FFI. This transfers ownership of the EnvVar, which should originate
|
||||
// in Box::into_raw().
|
||||
static env_var_t new_ffi(EnvVar *ptr);
|
||||
|
@ -163,33 +156,7 @@ class env_var_t {
|
|||
/// \return the character used when delimiting quoted expansion.
|
||||
wchar_t get_delimiter() const;
|
||||
|
||||
/// \return a copy of this variable with new values.
|
||||
env_var_t setting_vals(std::vector<wcstring> vals) const {
|
||||
return env_var_t{std::move(vals), get_flags()};
|
||||
}
|
||||
|
||||
env_var_t setting_exports(bool exportv) const {
|
||||
env_var_flags_t flags = get_flags();
|
||||
if (exportv) {
|
||||
flags |= flag_export;
|
||||
} else {
|
||||
flags &= ~flag_export;
|
||||
}
|
||||
return env_var_t{as_list(), flags};
|
||||
}
|
||||
|
||||
env_var_t setting_pathvar(bool pathvar) const {
|
||||
env_var_flags_t flags = get_flags();
|
||||
if (pathvar) {
|
||||
flags |= flag_pathvar;
|
||||
} else {
|
||||
flags &= ~flag_pathvar;
|
||||
}
|
||||
return env_var_t{as_list(), flags};
|
||||
}
|
||||
|
||||
static env_var_flags_t flags_for(const wchar_t *name);
|
||||
static std::shared_ptr<const std::vector<wcstring>> empty_list();
|
||||
|
||||
env_var_t &operator=(const env_var_t &);
|
||||
env_var_t &operator=(env_var_t &&) = default;
|
||||
|
@ -228,29 +195,22 @@ class environment_t {
|
|||
/// The null environment contains nothing.
|
||||
class null_environment_t : public environment_t {
|
||||
public:
|
||||
null_environment_t() = default;
|
||||
~null_environment_t() override;
|
||||
null_environment_t();
|
||||
~null_environment_t();
|
||||
|
||||
maybe_t<env_var_t> get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override;
|
||||
std::vector<wcstring> get_names(env_mode_flags_t flags) const override;
|
||||
|
||||
private:
|
||||
rust::Box<EnvNull> impl_;
|
||||
};
|
||||
|
||||
/// A mutable environment which allows scopes to be pushed and popped.
|
||||
class env_stack_impl_t;
|
||||
class env_stack_t final : public environment_t {
|
||||
friend class parser_t;
|
||||
|
||||
/// The implementation. Do not access this directly.
|
||||
std::unique_ptr<env_stack_impl_t> impl_;
|
||||
|
||||
/// All environment stacks are guarded by a global lock.
|
||||
acquired_lock<env_stack_impl_t> acquire_impl();
|
||||
acquired_lock<const env_stack_impl_t> acquire_impl() const;
|
||||
|
||||
explicit env_stack_t(std::unique_ptr<env_stack_impl_t> impl);
|
||||
|
||||
/// \return whether we are the principal stack.
|
||||
bool is_principal() const { return this == principal_ref().get(); }
|
||||
bool is_principal() const;
|
||||
|
||||
public:
|
||||
~env_stack_t() override;
|
||||
|
@ -334,6 +294,12 @@ class env_stack_t final : public environment_t {
|
|||
// Access a variable stack that only represents globals.
|
||||
// Do not push or pop from this.
|
||||
static env_stack_t &globals();
|
||||
|
||||
private:
|
||||
env_stack_t(rust::Box<EnvStackRef> imp);
|
||||
|
||||
/// The implementation. Do not access this directly.
|
||||
rust::Box<EnvStackRef> impl_;
|
||||
};
|
||||
|
||||
bool get_use_posix_spawn();
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// replacement. There is no logic still in here that needs to be ported to rust.
|
||||
|
||||
#ifndef FISH_EVENT_H
|
||||
#ifdef INCLUDE_RUST_HEADERS
|
||||
#define FISH_EVENT_H
|
||||
|
||||
#include <unistd.h>
|
||||
|
@ -13,11 +12,16 @@
|
|||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
#include "cxx.h"
|
||||
#include "global_safety.h"
|
||||
#include "wutil.h"
|
||||
|
||||
class parser_t;
|
||||
#if INCLUDE_RUST_HEADERS
|
||||
#include "event.rs.h"
|
||||
#else
|
||||
struct Event;
|
||||
#endif
|
||||
|
||||
/// The process id that is used to match any process id.
|
||||
// TODO: Remove after porting functions.cpp
|
||||
|
@ -34,4 +38,3 @@ void event_fire_generic(parser_t &parser, const wcstring &name,
|
|||
const std::vector<wcstring> &args = g_empty_string_list);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue