ronantakizawa/codereview-bench huggingface dataset:
Repository: sxyazi/yazi
File: yazi-dds/src/client.rs (Rust)

Code before:
						} else {
							Payload::from_str(&line).map(|p| p.emit()).ok();
						}
					}
				}
			}
		});
	}

	/// Connect to an existing server and listen in on the messages that are being
	/// sent by other yazi instances.
	/// If no server is running, fail right away.
	/// If a server is closed, attempt to reconnect forever.
	pub async fn echo_events_to_stdout(kinds: HashSet<String>) -> Result<()> {
		let mut lines = Self::connect_listener(&kinds).await?;

		loop {
			match lines.next_line().await {
				Ok(Some(s)) => {
					let kind = s.split(',').next();
					if matches!(kind, Some(kind) if kinds.contains(kind)) {
						println!("{}", s);
					}
				}
				Ok(None) => loop {
					println!("Connection closed");
					match Self::connect_listener(&kinds).await {
						Ok(new_lines) => {
							lines = new_lines;
							break;
						}
						Err(_) => {
							time::sleep(time::Duration::from_secs(1)).await;
						}
					};
				},
				Err(e) => {
					// could not establish initial connection
					return Err(e.into());
				}
			}
		}
	}

	async fn connect_listener(kinds: &HashSet<String>) -> Result<ClientReader> {
		let (lines, mut writer) = Stream::connect().await?;
		let hi = Payload::new(BodyHi::borrowed(kinds.iter().collect()));
		writer.write_all(format!("{}\n", hi).as_bytes()).await?;
		writer.flush().await?;
		Ok(lines)
	}

Review comment (suggestion):
```suggestion

```

Output not related to the protocol needs to be deleted

Code after:
						} else {
							Payload::from_str(&line).map(|p| p.emit()).ok();
						}
					}
				}
			}
		});
	}

	/// Connect to an existing server to send a single message.
	pub async fn shot(kind: &str, receiver: u64, severity: Option<u16>, body: &str) -> Result<()> {
		Body::validate(kind)?;

		let sender = severity.map(Into::into).unwrap_or(*ID);
		let payload = format!(
			"{}\n{kind},{receiver},{sender},{body}\n{}\n",
			Payload::new(BodyHi::borrowed(Default::default())),
			Payload::new(BodyBye::owned())
		);

		let (mut lines, mut writer) = Stream::connect().await?;
		writer.write_all(payload.as_bytes()).await?;
		writer.flush().await?;
		drop(writer);

		let mut version = None;
		while let Ok(Some(line)) = lines.next_line().await {
			match line.split(',').next() {
				Some("hey") if version.is_none() => {
					if let Ok(Body::Hey(hey)) = Payload::from_str(&line).map(|p| p.body) {
						version = Some(hey.version);
					}
				}
				Some("bye") => break,
				_ => {}
			}
		}

		if version != Some(BodyHi::version()) {
			bail!(
				"Incompatible version (Ya {}, Yazi {})",
				BodyHi::version(),
				version.as_deref().unwrap_or("Unknown")
			);
		}
		Ok(())
	}

	/// Connect to an existing server and listen in on the messages that are being
	/// sent by other yazi instances:
	///   - If no server is running, fail right away;
Repository: sxyazi/yazi
File: yazi-fm/src/components/manager.rs (Rust)

Code before:
	Outside,
}

impl Widget for Manager {
	fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) {
		let mut f = || {
			let area = Rect::cast(&LUA, area)?;
			let comp: Table = LUA.globals().raw_get("Manager")?;
			render_widgets(comp.call_method("render", area)?, buf);
			Ok::<_, anyhow::Error>(())
		};
		if let Err(e) = f() {
			error!("{:?}", e);
		}
	}
}

impl Manager {
	pub fn mouse(
		self,
		event: crossterm::event::MouseEvent,
		layout: &Layout,
		cx: &crate::context::Ctx,
	) {
		let position = Position { x: event.column, y: event.row };
		let comp = if layout.preview.contains(position) {
			ManagerComponent::Preview
		} else if layout.current.contains(position) {
			ManagerComponent::Current
		} else if layout.parent.contains(position) {
			ManagerComponent::Parent
		} else {
			ManagerComponent::Outside
		};

		let area = match event.kind {
			MouseEventKind::Moved | MouseEventKind::Drag(_) => layout.manager,
			_ => match comp {
				ManagerComponent::Parent => layout.parent,
				ManagerComponent::Current => layout.current,
				ManagerComponent::Preview => layout.preview,
				ManagerComponent::Outside => return,
			},
		};
		let event = crossterm::event::MouseEvent {
			kind:      event.kind,
			column:    event.column - area.x,
			row:       event.row - area.y,
			modifiers: event.modifiers,
		};
		let f = || {

Review comment (suggestion):
The `Manager` component should only include the logic for `Moved` and `Drag(MouseButton)`, all others should be dispatched to specific components (`Parent`, `Current`, `Preview`). There are no `parent.rs` and `current.rs` currently, please create them.

Code after:
use mlua::{Table, TableExt};
use ratatui::{buffer::Buffer, widgets::Widget};
use tracing::error;
use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA};

pub(crate) struct Manager;

impl Widget for Manager {
	fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) {
		let mut f = || {
			let area = Rect::cast(&LUA, area)?;
			let comp: Table = LUA.globals().raw_get("Manager")?;
			render_widgets(comp.call_method("render", area)?, buf);
			Ok::<_, anyhow::Error>(())
		};
		if let Err(e) = f() {
			error!("{:?}", e);
		}
	}
}
Repository: sxyazi/yazi
File: yazi-config/src/layout.rs (Rust)

Code before:
use ratatui::layout::Rect;

#[derive(Default)]
pub struct Layout {
	pub header: Rect,

	pub manager: Rect,

	pub parent:  Rect,
	pub current: Rect,
	pub preview: Rect,

	pub status: Rect,
}


Review comment (suggestion):
After dispatching events to specific components (`Current`, `Parent`, `Preview`), this will no longer be necessary, please remove it

Code after:
use ratatui::layout::Rect;

#[derive(Default)]
pub struct Layout {
	pub header: Rect,

	pub parent:  Rect,
	pub current: Rect,
	pub preview: Rect,

	pub status: Rect,
}
Repository: sxyazi/yazi
File: yazi-config/src/manager/manager.rs (Rust)

Code before:
use serde::{Deserialize, Serialize};
use validator::Validate;

use super::{ManagerRatio, SortBy};
use crate::{validation::check_validation, MERGED_YAZI};

#[derive(Debug, Deserialize, Serialize, Validate)]
pub struct Manager {
	pub ratio: ManagerRatio,

	// Sorting
	pub sort_by:        SortBy,
	pub sort_sensitive: bool,
	pub sort_reverse:   bool,
	pub sort_dir_first: bool,

	// Display
	#[validate(length(min = 1, max = 20, message = "must be between 1 and 20 characters"))]
	pub linemode:     String,
	pub show_hidden:  bool,
	pub show_symlink: bool,
	pub scrolloff:    u8,
	pub mouse:        bool,
}

impl Default for Manager {
	fn default() -> Self {
		#[derive(Deserialize)]
		struct Outer {
			manager: Manager,
		}

		let manager = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().manager;

		check_validation(manager.validate());
		manager
	}
}


Review comment (bug):
```suggestion
	pub mouse:        MouseEvents,
```

`MouseEvents` looks like:

```rust
bitflags! {
	#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
	#[serde(try_from = "Vec<String>")]
	pub struct MouseEvents: u8 {
		const CLICK  = 0b00001;
		const SCROLL = 0b00010;
		const TOUCH  = 0b00100;
		const MOVE   = 0b01000;
		const DRAG   = 0b10000;
	}
}
```

You can check out 

https://github.com/sxyazi/yazi/blob/28dfe728ab541f71be27f131e985723253bcff6e/yazi-config/src/popup/offset.rs#L13

 for implementing it.

Code after:
use serde::{Deserialize, Serialize};
use validator::Validate;

use super::{ManagerRatio, MouseEvents, SortBy};
use crate::{validation::check_validation, MERGED_YAZI};

#[derive(Debug, Deserialize, Serialize, Validate)]
pub struct Manager {
	pub ratio: ManagerRatio,

	// Sorting
	pub sort_by:        SortBy,
	pub sort_sensitive: bool,
	pub sort_reverse:   bool,
	pub sort_dir_first: bool,
	pub sort_translit:  bool,

	// Display
	#[validate(length(min = 1, max = 20, message = "must be between 1 and 20 characters"))]
	pub linemode:     String,
	pub show_hidden:  bool,
	pub show_symlink: bool,
	pub scrolloff:    u8,
	pub mouse_events: MouseEvents,
}

impl Default for Manager {
	fn default() -> Self {
		#[derive(Deserialize)]
		struct Outer {
			manager: Manager,
		}

		let manager = toml::from_str::<Outer>(&MERGED_YAZI).unwrap().manager;

		check_validation(manager.validate());
		manager
	}
}
Repository: sxyazi/yazi
File: yazi-plugin/src/external/highlighter.rs (Rust)

Code before:
	) -> Result<(Vec<String>, Vec<String>), PeekError> {
		let mut before = Vec::with_capacity(if *plain { 0 } else { skip });
		let mut after = Vec::with_capacity(area.height as usize);

		let mut long_lines = vec![];
		let mut buf = vec![];
		// If we want to indent plain text, we have to decide if it is plain
		while reader.read_until(b'\n', &mut buf).await.is_ok() {
			if buf.is_empty() {
				break;
			}
			if !*plain && buf.len() > MAX_LINE_BYTES_TO_PLAINTEXT_FALLBACK {
				*plain = true;
			}
			long_lines.push(buf.clone());
			buf.clear()
		}

		let mut lines_handled = 0;
		for mut long_line in long_lines {
			if long_line.is_empty() || lines_handled > skip + area.height as usize {
				break;
			}
			Self::replace_tabs_with_spaces(&mut long_line, PREVIEW.tab_size as usize);
			for line in Self::chunk_by_width(long_line, area.width as usize) {
				let mut line = line.to_vec();
				lines_handled += 1;
				let must_break = Self::handle_single_line(
					lines_handled,
					skip,
					*plain,
					area.height as usize,
					&mut line,
					&mut before,
					&mut after,
				);
				if must_break {
					break;
				}
			}
		}

		let no_more_scroll = lines_handled < skip + area.height as usize;
		if skip > 0 && no_more_scroll {
			return Err(PeekError::Exceed(lines_handled.saturating_sub(area.height as usize)));
		}

		Ok((before, after))
	}

	async fn before_after(

Review comment (suggestion):
```suggestion
			for mut line in Self::chunk_by_width(long_line, area.width as usize) {
```

This can avoid unnecessary memory reallocations.

Code after:
			Text::from(after.join("").replace('\x1b', "^[").replace('\t', &PREVIEW.indent()))
		} else {
			Self::highlight_with(before, after, syntax.unwrap()).await?
		})
	}

	async fn highlight_with(
		before: Vec<String>,
		after: Vec<String>,
		syntax: &'static SyntaxReference,
	) -> Result<Text<'static>, PeekError> {
		let ticket = INCR.load(Ordering::Relaxed);
		let (theme, syntaxes) = Self::init().await;

		tokio::task::spawn_blocking(move || {
			let mut h = HighlightLines::new(syntax, theme);
			for line in before {
				if ticket != INCR.load(Ordering::Relaxed) {
					return Err("Highlighting cancelled".into());
				}
				h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?;
			}

			let indent = PREVIEW.indent();
			let mut lines = Vec::with_capacity(after.len());
			for line in after {
				if ticket != INCR.load(Ordering::Relaxed) {
					return Err("Highlighting cancelled".into());
				}

				let regions = h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?;
				lines.push(Self::to_line_widget(regions, &indent));
			}

			Ok(Text::from(lines))
		})
		.await?
	}

	async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> {
		let (_, syntaxes) = Self::init().await;
		let name = path.file_name().map(|n| n.to_string_lossy()).unwrap_or_default();
		if let Some(s) = syntaxes.find_syntax_by_extension(&name) {
			return Ok(s);
		}

		let ext = path.extension().map(|e| e.to_string_lossy()).unwrap_or_default();
		if let Some(s) = syntaxes.find_syntax_by_extension(&ext) {
			return Ok(s);
		}
Repository: sxyazi/yazi
File: yazi-plugin/src/external/highlighter.rs (Rust)

Code before:
		} else {
			Self::highlight_with(before, after, syntax.unwrap()).await
		}
	}

	async fn before_after_wrapped(
		skip: usize,
		area: Rect,
		plain: &mut bool,
		mut reader: BufReader<File>,
	) -> Result<(Vec<String>, Vec<String>), PeekError> {
		let mut before = Vec::with_capacity(if *plain { 0 } else { skip });
		let mut after = Vec::with_capacity(area.height as usize);

		let mut long_lines = vec![];
		let mut buf = vec![];
		// If we want to indent plain text, we have to decide if it is plain
		while reader.read_until(b'\n', &mut buf).await.is_ok() {
			if buf.is_empty() {
				break;
			}
			if !*plain && buf.len() > MAX_LINE_BYTES_TO_PLAINTEXT_FALLBACK {
				*plain = true;
			}
			long_lines.push(mem::take(&mut buf));
			buf.clear()
		}

		let mut lines_handled = 0;
		for mut long_line in long_lines {
			if long_line.is_empty() || lines_handled > skip + area.height as usize {
				break;
			}
			Self::replace_tabs_with_spaces(&mut long_line, PREVIEW.tab_size as usize);
			for line in Self::chunk_by_width(long_line, area.width as usize) {
				lines_handled += 1;
				let must_break = Self::handle_single_line(
					lines_handled,
					skip,
					*plain,
					area.height as usize,
					line,
					&mut before,
					&mut after,
				);
				if must_break {
					break;
				}
			}
		}


Review comment (suggestion):
```suggestion

```

This is unnecessary. After `mem::take`, `buf` will be initialized as a new `Vec`.

Code after:
			if i > skip + area.height as usize {
				break;
			}
		}

		if skip > 0 && i < skip + area.height as usize {
			return Err(PeekError::Exceed(i.saturating_sub(area.height as _)));
		}

		Ok(if plain {
			Text::from(after.join("").replace('\x1b', "^[").replace('\t', &PREVIEW.indent()))
		} else {
			Self::highlight_with(before, after, syntax.unwrap()).await?
		})
	}

	async fn highlight_with(
		before: Vec<String>,
		after: Vec<String>,
		syntax: &'static SyntaxReference,
	) -> Result<Text<'static>, PeekError> {
		let ticket = INCR.load(Ordering::Relaxed);
		let (theme, syntaxes) = Self::init().await;

		tokio::task::spawn_blocking(move || {
			let mut h = HighlightLines::new(syntax, theme);
			for line in before {
				if ticket != INCR.load(Ordering::Relaxed) {
					return Err("Highlighting cancelled".into());
				}
				h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?;
			}

			let indent = PREVIEW.indent();
			let mut lines = Vec::with_capacity(after.len());
			for line in after {
				if ticket != INCR.load(Ordering::Relaxed) {
					return Err("Highlighting cancelled".into());
				}

				let regions = h.highlight_line(&line, syntaxes).map_err(|e| anyhow!(e))?;
				lines.push(Self::to_line_widget(regions, &indent));
			}

			Ok(Text::from(lines))
		})
		.await?
	}

	async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> {
		let (_, syntaxes) = Self::init().await;
Repository: sxyazi/yazi
File: yazi-plugin/src/external/highlighter.rs (Rust)

Code before:
		let no_more_scroll = lines_handled < skip + area.height as usize;
		if skip > 0 && no_more_scroll {
			return Err(PeekError::Exceed(lines_handled.saturating_sub(area.height as usize)));
		}

		Ok((before, after))
	}

	fn handle_single_line(
		lines_handled: usize,
		skip: usize,
		plain: bool,
		limit: usize,
		mut line: String,
		before: &mut Vec<String>,
		after: &mut Vec<String>,
	) -> bool {
		if line.is_empty() || lines_handled > skip + limit {
			return true;
		}

		if line.as_str().ends_with("\r\n") {
			let mut chars = line.chars();
			chars.next_back();
			chars.next_back();
			line = chars.as_str().to_string();
		} else if !line.as_str().ends_with('\n') {
			line.push('\n')
		}

		if lines_handled > skip {
			after.push(line);
		} else if !plain {
			before.push(line);
		}
		false
	}

	fn chunk_by_width(line: Vec<u8>, width: usize) -> Vec<String> {
		let line = String::from_utf8_lossy(&line);
		if line.width() <= width {
			return vec![line.to_string()];
		}

		let mut resulted_lines = vec![];
		// Use this buffer to calculate width
		let mut buf_line = String::with_capacity(width);
		// Use this buffer to slice line
		let mut buf_chars = Vec::with_capacity(width);
		let mut last_break_char_idx = 0;
		let mut last_break_idx = 0;

Review comment (suggestion):
Please keep the original code here. The new implementation using `to_string()` will cause unnecessary memory reallocations. Since `\r\n` are known to be ASCII characters, there's no need to treat them as Unicode `chars`.

Code after:
				content: Cow::Borrowed(s),
				..Default::default()
			}],
			..Default::default()
		};

		ratatui::widgets::Paragraph::new(line)
			.wrap(ratatui::widgets::Wrap { trim: false })
			.line_count(width)
	}

	#[inline(always)]
	fn carriage_return_to_line_feed(c: &mut u8) {
		if *c == b'\r' {
			*c = b'\n';
		}
	}
}

impl Highlighter {
	pub fn to_line_widget(regions: Vec<(highlighting::Style, &str)>, indent: &str) -> Line<'static> {
		let spans: Vec<_> = regions
			.into_iter()
			.map(|(style, s)| {
				let mut modifier = ratatui::style::Modifier::empty();
				if style.font_style.contains(highlighting::FontStyle::BOLD) {
					modifier |= ratatui::style::Modifier::BOLD;
				}
				if style.font_style.contains(highlighting::FontStyle::ITALIC) {
					modifier |= ratatui::style::Modifier::ITALIC;
				}
				if style.font_style.contains(highlighting::FontStyle::UNDERLINE) {
					modifier |= ratatui::style::Modifier::UNDERLINED;
				}

				Span {
					content: s.replace('\t', indent).into(),
					style:   ratatui::style::Style {
						fg: Self::to_ansi_color(style.foreground),
						// bg: Self::to_ansi_color(style.background),
						add_modifier: modifier,
						..Default::default()
					},
				}
			})
			.collect();

		Line::from(spans)
	}

	// Copy from https://github.com/sharkdp/bat/blob/master/src/terminal.rs
Repository: sxyazi/yazi
File: yazi-plugin/src/external/highlighter.rs (Rust)

Code before:
		if skip > 0 && no_more_scroll {
			return Err(PeekError::Exceed(lines_handled.saturating_sub(area.height as usize)));
		}

		Ok((before, after))
	}

	fn handle_single_line(
		lines_handled: usize,
		skip: usize,
		plain: bool,
		limit: usize,
		mut line: String,
		before: &mut Vec<String>,
		after: &mut Vec<String>,
	) -> bool {
		if line.is_empty() || lines_handled > skip + limit {
			return true;
		}

		if line.as_str().ends_with("\r\n") {
			let mut chars = line.chars();
			chars.next_back();
			chars.next_back();
			line = chars.as_str().to_string();
		} else if !line.as_str().ends_with('\n') {
			line.push('\n')
		}

		if lines_handled > skip {
			after.push(line);
		} else if !plain {
			before.push(line);
		}
		false
	}

	fn chunk_by_width(line: Vec<u8>, width: usize) -> Vec<String> {
		let line = String::from_utf8_lossy(&line);
		if line.width() <= width {
			return vec![line.to_string()];
		}

		let mut resulted_lines = vec![];
		// Use this buffer to calculate width
		let mut buf_line = String::with_capacity(width);
		// Use this buffer to slice line
		let mut buf_chars = Vec::with_capacity(width);
		let mut last_break_char_idx = 0;
		let mut last_break_idx = 0;
		for (i, char) in line.chars().enumerate() {

Review comment (suggestion):
```suggestion
		} else if !line.ends_with('\n') {
```

Code after:
				..Default::default()
			}],
			..Default::default()
		};

		ratatui::widgets::Paragraph::new(line)
			.wrap(ratatui::widgets::Wrap { trim: false })
			.line_count(width)
	}

	#[inline(always)]
	fn carriage_return_to_line_feed(c: &mut u8) {
		if *c == b'\r' {
			*c = b'\n';
		}
	}
}

impl Highlighter {
	pub fn to_line_widget(regions: Vec<(highlighting::Style, &str)>, indent: &str) -> Line<'static> {
		let spans: Vec<_> = regions
			.into_iter()
			.map(|(style, s)| {
				let mut modifier = ratatui::style::Modifier::empty();
				if style.font_style.contains(highlighting::FontStyle::BOLD) {
					modifier |= ratatui::style::Modifier::BOLD;
				}
				if style.font_style.contains(highlighting::FontStyle::ITALIC) {
					modifier |= ratatui::style::Modifier::ITALIC;
				}
				if style.font_style.contains(highlighting::FontStyle::UNDERLINE) {
					modifier |= ratatui::style::Modifier::UNDERLINED;
				}

				Span {
					content: s.replace('\t', indent).into(),
					style:   ratatui::style::Style {
						fg: Self::to_ansi_color(style.foreground),
						// bg: Self::to_ansi_color(style.background),
						add_modifier: modifier,
						..Default::default()
					},
				}
			})
			.collect();

		Line::from(spans)
	}

	// Copy from https://github.com/sharkdp/bat/blob/master/src/terminal.rs
	fn to_ansi_color(color: highlighting::Color) -> Option<ratatui::style::Color> {
Repository: sxyazi/yazi
File: yazi-scheduler/src/file/file.rs (Rust)

Code before:
			FileOp::Link(task) => {
				let meta = task.meta.as_ref().unwrap();

				let src = if task.resolve {
					match fs::read_link(&task.from).await {
						Ok(p) => Cow::Owned(p),
						Err(e) if e.kind() == NotFound => {
							self.log(task.id, format!("Link task partially done: {:?}", task))?;
							return Ok(self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?);
						}
						Err(e) => Err(e)?,
					}
				} else {
					Cow::Borrowed(task.from.as_path())
				};

				let src = if task.relative {
					path_relative_to(&src, &fs::canonicalize(task.to.parent().unwrap()).await?)
				} else {
					src
				};

				ok_or_not_found(fs::remove_file(&task.to).await)?;

				if task.hard {
					if !src.is_symlink() && src.is_dir() {
						hard_link_directory(src, &task.to).await?;
					} else {
						fs::hard_link(src, &task.to).await?;
					}
				} else {
					#[cfg(unix)]
					{
						fs::symlink(src, &task.to).await?
					}
					#[cfg(windows)]
					{
						if meta.is_dir() {
							fs::symlink_dir(src, &task.to).await?
						} else {
							fs::symlink_file(src, &task.to).await?
						}
					}
				}

				if task.delete {
					fs::remove_file(&task.from).await.ok();
				}
				self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?;
			}
			FileOp::Delete(task) => {

Review comment (suggestion):
I added `!src.is_symlink()` here to cover the special case where the yanked file is a symlink to a directory. With this version, a hardlink of the symlink is created instead of following the original symlink and hardlinking the destination.

Regular directories and symlinks to non-directory files were working correctly even before this change.

Code after:
				let meta = task.meta.as_ref().unwrap();

				let src = if task.resolve {
					match fs::read_link(&task.from).await {
						Ok(p) => Cow::Owned(p),
						Err(e) if e.kind() == NotFound => {
							warn!("Link task partially done: {task:?}");
							return Ok(self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?);
						}
						Err(e) => Err(e)?,
					}
				} else {
					Cow::Borrowed(task.from.as_path())
				};

				let src = if task.relative {
					path_relative_to(&src, &fs::canonicalize(task.to.parent().unwrap()).await?)
				} else {
					src
				};

				ok_or_not_found(fs::remove_file(&task.to).await)?;
				#[cfg(unix)]
				{
					fs::symlink(src, &task.to).await?;
				}
				#[cfg(windows)]
				{
					if meta.is_dir() {
						fs::symlink_dir(src, &task.to).await?;
					} else {
						fs::symlink_file(src, &task.to).await?;
					}
				}

				if task.delete {
					fs::remove_file(&task.from).await.ok();
				}
				self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?;
			}
			FileOp::Hardlink(task) => {
				let meta = task.meta.as_ref().unwrap();
				let src = if !task.follow {
					Cow::Borrowed(task.from.as_path())
				} else if let Ok(p) = fs::canonicalize(&task.from).await {
					Cow::Owned(p)
				} else {
					Cow::Borrowed(task.from.as_path())
				};

				ok_or_not_found(fs::remove_file(&task.to).await)?;