mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
..
This commit is contained in:
parent
46c54f6bad
commit
4718c4ddee
15 changed files with 272 additions and 92 deletions
|
|
@ -76,7 +76,7 @@ impl App {
|
|||
fn dispatch_render(&mut self) {
|
||||
if let Some(term) = &mut self.term {
|
||||
let _ = term.draw(|f| {
|
||||
plugin::scope(&self.cx, || {
|
||||
plugin::scope(&self.cx, |_| {
|
||||
f.render_widget(Root::new(&self.cx), f.size());
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -16,36 +16,14 @@ impl Folder {
|
|||
}
|
||||
|
||||
impl Folder {
|
||||
// fn highlighted_item<'b>(&'b self, file: &'b File) -> Vec<Span> {
|
||||
// let short = short_path(file.url(), &self.folder.cwd);
|
||||
//
|
||||
// let v = self.is_find.then_some(()).and_then(|_| {
|
||||
// let finder = self.cx.manager.active().finder()?;
|
||||
// let (head, body, tail) = finder.explode(short.name)?;
|
||||
//
|
||||
// // TODO: to be configured by THEME?
|
||||
// let style = Style::new().fg(Color::Rgb(255, 255,
|
||||
// 50)).add_modifier(Modifier::ITALIC); Some(vec![
|
||||
// Span::raw(short.prefix.join(head.as_ref()).display().to_string()),
|
||||
// Span::styled(body, style),
|
||||
// Span::raw(tail),
|
||||
// ])
|
||||
// });
|
||||
//
|
||||
// v.unwrap_or_else(|| vec![Span::raw(format!("{}", short))])
|
||||
// }
|
||||
}
|
||||
|
||||
impl Widget for Folder {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let x = plugin::Folder { kind: self.kind as u8 }.render(area);
|
||||
let x = plugin::Folder { kind: self.kind as u8 }.render(area, buf);
|
||||
if x.is_err() {
|
||||
info!("{:?}", x);
|
||||
return;
|
||||
}
|
||||
|
||||
for x in x.unwrap() {
|
||||
x.render(buf);
|
||||
}
|
||||
|
||||
// let items: Vec<_> = window
|
||||
|
|
|
|||
|
|
@ -5,14 +5,10 @@ pub(crate) struct Layout;
|
|||
|
||||
impl Widget for Layout {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let x = plugin::Status::render(area);
|
||||
let x = plugin::Status.render(area, buf);
|
||||
if x.is_err() {
|
||||
info!("{:?}", x);
|
||||
return;
|
||||
}
|
||||
|
||||
for x in x.unwrap() {
|
||||
x.render(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::BTreeMap, ffi::OsStr};
|
||||
use std::{collections::BTreeMap, ffi::OsStr, ops::Range};
|
||||
|
||||
use anyhow::Result;
|
||||
use regex::bytes::{Regex, RegexBuilder};
|
||||
|
|
@ -91,13 +91,17 @@ impl Finder {
|
|||
|
||||
/// Explode the name into three parts: head, body, tail.
|
||||
#[inline]
|
||||
pub fn explode(&self, name: &[u8]) -> Option<(String, String, String)> {
|
||||
let range = self.query.find(name).map(|m| m.range())?;
|
||||
Some((
|
||||
String::from_utf8_lossy(&name[..range.start]).to_string(),
|
||||
String::from_utf8_lossy(&name[range.start..range.end]).to_string(),
|
||||
String::from_utf8_lossy(&name[range.end..]).to_string(),
|
||||
))
|
||||
pub fn highlighted(&self, name: &OsStr) -> Vec<Range<usize>> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let found = self.query.find(name.to_string_lossy().as_bytes());
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let found = {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
self.query.find(name.as_bytes())
|
||||
};
|
||||
|
||||
found.map(|m| vec![m.range()]).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@ function Folder:by_kind(kind)
|
|||
end
|
||||
end
|
||||
|
||||
function Folder:window(kind) return (self:by_kind(kind) or {}).window end
|
||||
|
||||
function Folder:hovered(kind) return (self:by_kind(kind) or {}).hovered end
|
||||
|
||||
function Folder:markers(area, markers)
|
||||
if #markers == 0 then
|
||||
return {}
|
||||
|
|
@ -51,50 +47,47 @@ function Folder:markers(area, markers)
|
|||
end
|
||||
|
||||
function Folder:parent(area)
|
||||
local window = self:window(self.Kind.Parent)
|
||||
if window == nil then
|
||||
local folder = self:by_kind(self.Kind.Parent)
|
||||
if folder == nil then
|
||||
return {}
|
||||
end
|
||||
|
||||
local hovered = (self:hovered(self.Kind.Parent) or {}).url
|
||||
local lines = {}
|
||||
for _, f in ipairs(window) do
|
||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
|
||||
if f.url == hovered then
|
||||
line = line:style(THEME.files.hovered)
|
||||
local items = {}
|
||||
for _, f in ipairs(folder.window) do
|
||||
local item = ui.ListItem(" " .. f:icon() .. " " .. f.name .. " ")
|
||||
if f.hovered then
|
||||
item = item:style(THEME.files.hovered)
|
||||
else
|
||||
line = line:style(f:style())
|
||||
item = item:style(f:style())
|
||||
end
|
||||
|
||||
lines[#lines + 1] = line
|
||||
items[#items + 1] = item
|
||||
end
|
||||
|
||||
return { ui.Paragraph(area, lines) }
|
||||
return { ui.List(area, items) }
|
||||
end
|
||||
|
||||
function Folder:current(area)
|
||||
local hovered = (self:hovered(self.Kind.Current) or {}).url
|
||||
local markers = {}
|
||||
local lines = {}
|
||||
for i, f in ipairs(self:window(self.Kind.Current)) do
|
||||
local name = f.name
|
||||
local items = {}
|
||||
for i, f in ipairs(self:by_kind(self.Kind.Current).window) do
|
||||
local name = ui.highlight_ranges(f.name, f:highlights())
|
||||
|
||||
-- Show symlink target
|
||||
if MANAGER.show_symlink then
|
||||
local link_to = f.link_to
|
||||
if link_to ~= nil then
|
||||
name = name .. " -> " .. tostring(link_to)
|
||||
if f.link_to ~= nil then
|
||||
name[#name + 1] = ui.Span(" -> " .. tostring(f.link_to)):italic()
|
||||
end
|
||||
end
|
||||
|
||||
-- Highlight hovered file
|
||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. name .. " ") }
|
||||
if f.url == hovered then
|
||||
line = line:style(THEME.files.hovered)
|
||||
local item = ui.ListItem(ui.Line { ui.Span(" " .. f:icon() .. " "), table.unpack(name) })
|
||||
if f.hovered then
|
||||
item = item:style(THEME.files.hovered)
|
||||
else
|
||||
line = line:style(f:style())
|
||||
item = item:style(f:style())
|
||||
end
|
||||
lines[#lines + 1] = line
|
||||
items[#items + 1] = item
|
||||
|
||||
-- Mark selected/yanked files
|
||||
if f:selected() then
|
||||
|
|
@ -102,28 +95,27 @@ function Folder:current(area)
|
|||
end
|
||||
end
|
||||
|
||||
return { ui.Paragraph(area, lines), table.unpack(self:markers(area, markers)) }
|
||||
return { ui.List(area, items), table.unpack(self:markers(area, markers)) }
|
||||
end
|
||||
|
||||
function Folder:preview(area)
|
||||
local window = self:window(self.Kind.Preview)
|
||||
if window == nil then
|
||||
local folder = self:by_kind(self.Kind.Preview)
|
||||
if folder == nil then
|
||||
return {}
|
||||
end
|
||||
|
||||
local hovered = (self:hovered(self.Kind.Preview) or {}).url
|
||||
local lines = {}
|
||||
for _, f in ipairs(window) do
|
||||
local line = ui.Line { ui.Span(" " .. f:icon() .. " " .. f.name .. " ") }
|
||||
if f.url == hovered then
|
||||
line = line:style(THEME.preview.hovered)
|
||||
local items = {}
|
||||
for _, f in ipairs(folder.window) do
|
||||
local item = ui.ListItem(" " .. f:icon() .. " " .. f.name .. " ")
|
||||
if f.hovered then
|
||||
item = item:style(THEME.preview.hovered)
|
||||
else
|
||||
line = line:style(f:style())
|
||||
item = item:style(f:style())
|
||||
end
|
||||
lines[#lines + 1] = line
|
||||
items[#items + 1] = item
|
||||
end
|
||||
|
||||
return { ui.Paragraph(area, lines) }
|
||||
return { ui.List(area, items) }
|
||||
end
|
||||
|
||||
function Folder:render(area, args)
|
||||
|
|
|
|||
|
|
@ -9,3 +9,24 @@ ui = {
|
|||
VERTICAL = true,
|
||||
},
|
||||
}
|
||||
|
||||
function ui.highlight_ranges(s, ranges)
|
||||
if ranges == nil or #ranges == 0 then
|
||||
return { ui.Span(s) }
|
||||
end
|
||||
|
||||
local spans = {}
|
||||
local last = 0
|
||||
for _, r in ipairs(ranges) do
|
||||
if r[1] > last then
|
||||
spans[#spans + 1] = ui.Span(s:sub(last + 1, r[1]))
|
||||
end
|
||||
-- TODO: use a customable style
|
||||
spans[#spans + 1] = ui.Span(s:sub(r[1] + 1, r[2])):fg("yellow"):italic()
|
||||
last = r[2]
|
||||
end
|
||||
if last < #s then
|
||||
spans[#spans + 1] = ui.Span(s:sub(last + 1))
|
||||
end
|
||||
return spans
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,23 @@
|
|||
use mlua::{MetaMethod, UserData, UserDataRef};
|
||||
use mlua::{IntoLua, MetaMethod, UserData, UserDataRef};
|
||||
|
||||
// --- Range
|
||||
pub struct Range<T>(std::ops::Range<T>);
|
||||
|
||||
impl<T> From<std::ops::Range<T>> for Range<T> {
|
||||
fn from(value: std::ops::Range<T>) -> Self { Self(value) }
|
||||
}
|
||||
|
||||
impl<'lua, T> IntoLua<'lua> for Range<T>
|
||||
where
|
||||
T: IntoLua<'lua>,
|
||||
{
|
||||
fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
let tbl = lua.create_sequence_from([self.0.start, self.0.end])?;
|
||||
tbl.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Url
|
||||
pub struct Url(shared::Url);
|
||||
|
||||
impl From<&shared::Url> for Url {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use core::Ctx;
|
|||
use config::{MANAGER, THEME};
|
||||
use mlua::{AnyUserData, Function, IntoLua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value};
|
||||
|
||||
use super::Url;
|
||||
use super::{Range, Url};
|
||||
use crate::{layout::Style, LUA};
|
||||
|
||||
struct File(core::files::File);
|
||||
|
|
@ -89,9 +89,13 @@ impl<'a, 'b> Tab<'a, 'b> {
|
|||
reg.add_function("style", |_, me: AnyUserData| {
|
||||
me.named_user_value::<Function>("style")?.call::<_, Style>(())
|
||||
});
|
||||
reg.add_field_function_get("hovered", |_, me| me.named_user_value::<bool>("hovered"));
|
||||
reg.add_function("selected", |_, me: AnyUserData| {
|
||||
me.named_user_value::<Function>("selected")?.call::<_, bool>(me)
|
||||
});
|
||||
reg.add_function("highlights", |_, me: AnyUserData| {
|
||||
me.named_user_value::<Function>("highlights")?.call::<_, Value>(())
|
||||
});
|
||||
|
||||
reg.add_field_method_get("url", |_, me| Ok(Url::from(me.url())));
|
||||
reg.add_field_method_get("length", |_, me| Ok(me.length()));
|
||||
|
|
@ -203,6 +207,11 @@ impl<'a, 'b> Tab<'a, 'b> {
|
|||
})?,
|
||||
)?;
|
||||
|
||||
ud.set_named_user_value(
|
||||
"hovered",
|
||||
matches!(&folder.hovered, Some(f) if f.url() == inner.url()),
|
||||
)?;
|
||||
|
||||
ud.set_named_user_value(
|
||||
"selected",
|
||||
self.scope.create_function(|_, me: AnyUserData| {
|
||||
|
|
@ -217,6 +226,20 @@ impl<'a, 'b> Tab<'a, 'b> {
|
|||
})?,
|
||||
)?;
|
||||
|
||||
ud.set_named_user_value(
|
||||
"highlights",
|
||||
self.scope.create_function(|_, ()| {
|
||||
let Some(finder) = self.inner.finder() else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(
|
||||
inner
|
||||
.name()
|
||||
.map(|n| finder.highlighted(n).into_iter().map(Range::from).collect::<Vec<_>>()),
|
||||
)
|
||||
})?,
|
||||
)?;
|
||||
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,30 +1,56 @@
|
|||
use mlua::{Result, Table, TableExt};
|
||||
use ratatui::layout;
|
||||
use mlua::{AnyUserData, Table, TableExt};
|
||||
|
||||
use crate::{layout::{Paragraph, Rect}, GLOBALS, LUA};
|
||||
use crate::{layout::{List, Paragraph, Rect}, GLOBALS, LUA};
|
||||
|
||||
#[inline]
|
||||
fn layout(values: Vec<AnyUserData>, buf: &mut ratatui::prelude::Buffer) -> mlua::Result<()> {
|
||||
for value in values {
|
||||
if let Ok(c) = value.take::<Paragraph>() {
|
||||
c.render(buf)
|
||||
} else if let Ok(c) = value.take::<List>() {
|
||||
c.render(buf)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- Status
|
||||
pub struct Status;
|
||||
|
||||
impl Status {
|
||||
pub fn render(area: layout::Rect) -> Result<Vec<Paragraph>> {
|
||||
pub fn render(
|
||||
self,
|
||||
area: ratatui::layout::Rect,
|
||||
buf: &mut ratatui::prelude::Buffer,
|
||||
) -> mlua::Result<()> {
|
||||
let comp: Table = GLOBALS.get("Status")?;
|
||||
comp.call_method::<_, _>("render", Rect(area))
|
||||
let values: Vec<AnyUserData> = comp.call_method::<_, _>("render", Rect(area))?;
|
||||
|
||||
layout(values, buf)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Folder
|
||||
pub struct Folder {
|
||||
pub kind: u8,
|
||||
}
|
||||
|
||||
impl Folder {
|
||||
fn args(&self) -> Result<Table> {
|
||||
fn args(&self) -> mlua::Result<Table> {
|
||||
let tbl = LUA.create_table()?;
|
||||
tbl.set("kind", self.kind)?;
|
||||
Ok(tbl)
|
||||
}
|
||||
|
||||
pub fn render(self, area: layout::Rect) -> Result<Vec<Paragraph>> {
|
||||
pub fn render(
|
||||
self,
|
||||
area: ratatui::layout::Rect,
|
||||
buf: &mut ratatui::prelude::Buffer,
|
||||
) -> mlua::Result<()> {
|
||||
let comp: Table = GLOBALS.get("Folder")?;
|
||||
comp.call_method::<_, _>("render", (Rect(area), self.args()?))
|
||||
let values: Vec<AnyUserData> =
|
||||
comp.call_method::<_, _>("render", (Rect(area), self.args()?))?;
|
||||
|
||||
layout(values, buf)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
117
plugin/src/layout/list.rs
Normal file
117
plugin/src/layout/list.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
use mlua::{AnyUserData, FromLua, Lua, Table, UserData, Value};
|
||||
use ratatui::widgets::Widget;
|
||||
|
||||
use super::{Line, Rect, Span, Style};
|
||||
use crate::{GLOBALS, LUA};
|
||||
|
||||
// --- List
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct List {
|
||||
area: ratatui::layout::Rect,
|
||||
|
||||
inner: ratatui::widgets::List<'static>,
|
||||
}
|
||||
|
||||
impl List {
|
||||
pub(crate) fn install() -> mlua::Result<()> {
|
||||
let ui: Table = GLOBALS.get("ui")?;
|
||||
ui.set(
|
||||
"List",
|
||||
LUA.create_function(|_, (area, items): (Rect, Vec<ListItem>)| {
|
||||
let items: Vec<_> = items.into_iter().map(|x| x.into()).collect();
|
||||
Ok(Self { area: area.0, inner: ratatui::widgets::List::new(items) })
|
||||
})?,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn render(self, buf: &mut ratatui::buffer::Buffer) {
|
||||
self.inner.render(self.area, buf);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for List {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> mlua::Result<Self> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(mlua::Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "List",
|
||||
message: Some("expected a List".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for List {}
|
||||
|
||||
// --- ListItem
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ListItem {
|
||||
content: ratatui::text::Text<'static>,
|
||||
style: Option<ratatui::style::Style>,
|
||||
}
|
||||
|
||||
impl ListItem {
|
||||
pub(crate) fn install() -> mlua::Result<()> {
|
||||
let ui: Table = GLOBALS.get("ui")?;
|
||||
ui.set(
|
||||
"ListItem",
|
||||
LUA.create_function(|_, value: Value| {
|
||||
match value {
|
||||
Value::UserData(ud) => {
|
||||
let content: ratatui::text::Text = if let Ok(line) = ud.take::<Line>() {
|
||||
line.0.into()
|
||||
} else if let Ok(span) = ud.take::<Span>() {
|
||||
span.0.into()
|
||||
} else {
|
||||
return Err(mlua::Error::external("expected a String, Line or Span"));
|
||||
};
|
||||
return Ok(Self { content, style: None });
|
||||
}
|
||||
Value::String(s) => {
|
||||
return Ok(Self { content: s.to_str()?.to_string().into(), style: None });
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Err(mlua::Error::external("expected a String, Line or Span"))
|
||||
})?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ListItem> for ratatui::widgets::ListItem<'static> {
|
||||
fn from(value: ListItem) -> Self {
|
||||
let mut item = Self::new(value.content);
|
||||
if let Some(style) = value.style {
|
||||
item = item.style(style)
|
||||
}
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for ListItem {
|
||||
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> mlua::Result<Self> {
|
||||
match value {
|
||||
Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
_ => Err(mlua::Error::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "ListItem",
|
||||
message: Some("expected a ListItem".to_string()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserData for ListItem {
|
||||
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
|
||||
ud.borrow_mut::<Self>()?.style = match value {
|
||||
Value::Nil => None,
|
||||
Value::Table(tbl) => Some(Style::from(tbl).0),
|
||||
Value::UserData(ud) => Some(ud.borrow::<Style>()?.0),
|
||||
_ => return Err(mlua::Error::external("expected a Style or Table or nil")),
|
||||
};
|
||||
Ok(ud)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
mod constraint;
|
||||
mod layout;
|
||||
mod line;
|
||||
mod list;
|
||||
mod paragraph;
|
||||
mod rect;
|
||||
mod span;
|
||||
|
|
@ -11,6 +12,7 @@ mod style;
|
|||
pub(super) use constraint::*;
|
||||
pub(super) use layout::*;
|
||||
pub(super) use line::*;
|
||||
pub(super) use list::*;
|
||||
pub(super) use paragraph::*;
|
||||
pub(super) use rect::*;
|
||||
pub(super) use span::*;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl Paragraph {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn render(self, buf: &mut ratatui::buffer::Buffer) {
|
||||
pub(crate) fn render(self, buf: &mut ratatui::buffer::Buffer) {
|
||||
let mut p = ratatui::widgets::Paragraph::new(self.text);
|
||||
if let Some(style) = self.style {
|
||||
p = p.style(style);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ pub fn init() {
|
|||
layout::Constraint::install()?;
|
||||
layout::Layout::install()?;
|
||||
layout::Line::install()?;
|
||||
layout::List::install()?;
|
||||
layout::ListItem::install()?;
|
||||
layout::Paragraph::install()?;
|
||||
layout::Rect::install()?;
|
||||
layout::Span::install()?;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
use core::Ctx;
|
||||
|
||||
pub use mlua::Scope;
|
||||
|
||||
use crate::{bindings, GLOBALS, LUA};
|
||||
|
||||
pub fn scope<F: FnOnce()>(cx: &Ctx, f: F) {
|
||||
pub fn scope<'a>(cx: &'a Ctx, f: impl FnOnce(&Scope<'a, 'a>)) {
|
||||
let _ = LUA.scope(|scope| {
|
||||
let tbl = LUA.create_table()?;
|
||||
tbl.set("active", bindings::Tab::new(scope, cx, cx.manager.active()).make()?)?;
|
||||
tbl.set("tasks", bindings::Tasks::make(scope, &cx.tasks)?)?;
|
||||
GLOBALS.set("cx", tbl)?;
|
||||
|
||||
Ok(f())
|
||||
Ok(f(scope))
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,10 +95,9 @@ pub fn copy_with_progress(from: &Path, to: &Path) -> mpsc::Receiver<Result<u64,
|
|||
#[allow(clippy::collapsible_else_if)]
|
||||
pub fn permissions(permissions: Permissions) -> Option<String> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
return None;
|
||||
}
|
||||
return None;
|
||||
|
||||
#[cfg(unix)]
|
||||
Some({
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue