diff --git a/judge-control-app/src/custom_file.rs b/judge-control-app/src/custom_file.rs index f6ac8fc..aa1f223 100644 --- a/judge-control-app/src/custom_file.rs +++ b/judge-control-app/src/custom_file.rs @@ -1 +1,3 @@ pub mod traits; + +pub mod text_file; diff --git a/judge-control-app/src/custom_file/text_file.rs b/judge-control-app/src/custom_file/text_file.rs new file mode 100644 index 0000000..3741f07 --- /dev/null +++ b/judge-control-app/src/custom_file/text_file.rs @@ -0,0 +1,77 @@ +#[cfg(test)] +mod tests; + +use std::{ + io::Write, + path::PathBuf, + sync::{Arc, RwLock}, +}; + +use super::traits::File; + +struct FileAccess; + +struct TextFileInner { + path: PathBuf, + _parent: Option, + lock: Arc>, +} + +impl Drop for TextFileInner { + fn drop(&mut self) { + let _ = std::fs::remove_file(&self.path); + // ./text_file/tests.rs でのテスト用 + eprintln!("{:?}", self.path); + } +} + +// TextFileInner を Arc でラップして使う. +// +// シンボリックリンク自体を子,リンク先を親と表現するとき,子は Arc<親> を持つ. +// (これにより子から drop することが保証されるはず?) +// +// オリジナルファイルには Arc> を新たに作成して持たせる. +// シンボリックリンク(子や孫すべて)にはその clone を持たせる. +// (元ファイルを根とする根付き木全体で 1 つの RwLock を共有しているはず?) +#[derive(Clone)] +struct TextFile(Arc); + +impl File for TextFile { + type InitArgs = String; + fn new(path: PathBuf, args: Self::InitArgs) -> anyhow::Result { + // ファイルの作成・初期化 + let mut file = std::fs::File::create(&path)?; + file.write_all(args.as_bytes())?; + + // オリジナルファイルなので,親は存在しない. + // Arc> を新たに作成して持たせる. + let inner = TextFileInner { + path, + _parent: None, + lock: Arc::new(RwLock::new(FileAccess)), + }; + + Ok(TextFile(Arc::new(inner))) + } + fn create_symlink_to(&self, path: PathBuf) -> anyhow::Result { + // シンボリックリンクの作成 + std::os::unix::fs::symlink(&self.0.path, &path)?; + + // 自身の clone を子に持たせる. + // Arc> の clone を持たせる. + let inner = TextFileInner { + path, + _parent: Some(self.clone()), + lock: self.0.lock.clone(), + }; + + Ok(TextFile(Arc::new(inner))) + } +} + +impl Drop for TextFile { + fn drop(&mut self) { + // Arc が drop したときに呼び出すと,複数回呼び出されてしまう. + // TextFileInner が drop したときに呼び出されるようにしたい. + } +} diff --git a/judge-control-app/src/custom_file/text_file/tests.rs b/judge-control-app/src/custom_file/text_file/tests.rs new file mode 100644 index 0000000..2765d7c --- /dev/null +++ b/judge-control-app/src/custom_file/text_file/tests.rs @@ -0,0 +1,29 @@ +use std::path::PathBuf; + +use crate::custom_file::traits::File; + +use super::TextFile; + +#[test] +fn drop_from_leaves() -> anyhow::Result<()> { + let v = { + let a = TextFile::new(PathBuf::from("a.txt"), "hello!".to_string())?; + + let b1 = a.create_symlink_to(PathBuf::from("b1.txt"))?; + let b2 = a.create_symlink_to(PathBuf::from("b2.txt"))?; + let b3 = a.create_symlink_to(PathBuf::from("b3.txt"))?; + + let c1 = b1.create_symlink_to(PathBuf::from("c1.txt"))?; + let c2 = b1.create_symlink_to(PathBuf::from("c2.txt"))?; + let c3 = b1.create_symlink_to(PathBuf::from("c3.txt"))?; + + vec![a, b1, b2, b3, c1, c2, c3]; + }; + + // たとえば + // "b2.txt"→ "b3.txt"→ "c1.txt"→ "c2.txt"→ "c3.txt"→ "b1.txt"→ "a.txt" + // のように,葉から drop することが確認できる. + drop(v); + + Ok(()) +} diff --git a/judge-control-app/src/custom_file/traits.rs b/judge-control-app/src/custom_file/traits.rs index 2646759..c3b4a2e 100644 --- a/judge-control-app/src/custom_file/traits.rs +++ b/judge-control-app/src/custom_file/traits.rs @@ -6,14 +6,14 @@ use uuid::Uuid; pub trait File: Sized + Drop { type InitArgs; fn new(path: PathBuf, args: Self::InitArgs) -> Result; - fn create_hardlink_to(&self, path: PathBuf) -> Result; + fn create_symlink_to(&self, path: PathBuf) -> Result; } pub trait FileFactory: Sized { fn new(path: PathBuf) -> Result; fn create_file(&self, uuid: Uuid, args: FileType::InitArgs) -> Result; - fn create_hardlink_of( + fn create_symlink_of( &self, uuid: Uuid, original: &FileType,