EBML: Rename TagType::Ebml -> TagType::Matroska

This commit is contained in:
Serial 2024-10-13 21:01:35 -04:00
parent 0cf31014b4
commit f4542bbfca
No known key found for this signature in database
GPG key ID: DA95198DC17C4568
16 changed files with 69 additions and 52 deletions

View file

@ -19,8 +19,8 @@ pub use vint::*;
#[lofty(internal_write_module_do_not_use_anywhere_else)] #[lofty(internal_write_module_do_not_use_anywhere_else)]
pub struct EbmlFile { pub struct EbmlFile {
/// An EBML tag /// An EBML tag
#[lofty(tag_type = "Ebml")] #[lofty(tag_type = "Matroska")]
pub(crate) ebml_tag: Option<EbmlTag>, pub(crate) ebml_tag: Option<MatroskaTag>,
/// The file's audio properties /// The file's audio properties
pub(crate) properties: EbmlProperties, pub(crate) properties: EbmlProperties,
} }

View file

@ -2,7 +2,7 @@ use super::{segment_attachments, segment_info, segment_tags, segment_tracks};
use crate::config::ParseOptions; use crate::config::ParseOptions;
use crate::ebml::element_reader::{ElementHeader, ElementIdent, ElementReader, ElementReaderYield}; use crate::ebml::element_reader::{ElementHeader, ElementIdent, ElementReader, ElementReaderYield};
use crate::ebml::properties::EbmlProperties; use crate::ebml::properties::EbmlProperties;
use crate::ebml::tag::EbmlTag; use crate::ebml::tag::MatroskaTag;
use crate::ebml::ElementId; use crate::ebml::ElementId;
use crate::error::Result; use crate::error::Result;
@ -12,7 +12,7 @@ pub(super) fn read_from<R>(
element_reader: &mut ElementReader<R>, element_reader: &mut ElementReader<R>,
parse_options: ParseOptions, parse_options: ParseOptions,
properties: &mut EbmlProperties, properties: &mut EbmlProperties,
) -> Result<Option<EbmlTag>> ) -> Result<Option<MatroskaTag>>
where where
R: Read + Seek, R: Read + Seek,
{ {

View file

@ -2,7 +2,7 @@ use crate::config::ParseOptions;
use crate::ebml::element_reader::{ use crate::ebml::element_reader::{
ElementChildIterator, ElementIdent, ElementReader, ElementReaderYield, ElementChildIterator, ElementIdent, ElementReader, ElementReaderYield,
}; };
use crate::ebml::{AttachedFile, EbmlTag}; use crate::ebml::{AttachedFile, MatroskaTag};
use crate::error::Result; use crate::error::Result;
use crate::macros::decode_err; use crate::macros::decode_err;
use crate::picture::MimeType; use crate::picture::MimeType;
@ -13,7 +13,7 @@ use std::io::{Read, Seek};
pub(super) fn read_from<R>( pub(super) fn read_from<R>(
children_reader: &mut ElementChildIterator<'_, R>, children_reader: &mut ElementChildIterator<'_, R>,
_parse_options: ParseOptions, _parse_options: ParseOptions,
tag: &mut EbmlTag, tag: &mut MatroskaTag,
) -> Result<()> ) -> Result<()>
where where
R: Read + Seek, R: Read + Seek,

View file

@ -1,6 +1,6 @@
use crate::config::ParseOptions; use crate::config::ParseOptions;
use crate::ebml::element_reader::ElementChildIterator; use crate::ebml::element_reader::ElementChildIterator;
use crate::ebml::EbmlTag; use crate::ebml::MatroskaTag;
use crate::error::Result; use crate::error::Result;
use std::io::{Read, Seek}; use std::io::{Read, Seek};
@ -9,7 +9,7 @@ use std::io::{Read, Seek};
pub(super) fn read_from<R>( pub(super) fn read_from<R>(
_children_reader: &mut ElementChildIterator<'_, R>, _children_reader: &mut ElementChildIterator<'_, R>,
_parse_options: ParseOptions, _parse_options: ParseOptions,
_tag: &mut EbmlTag, _tag: &mut MatroskaTag,
) -> Result<()> ) -> Result<()>
where where
R: Read + Seek, R: Read + Seek,

View file

@ -1,6 +1,6 @@
use crate::config::ParseOptions; use crate::config::ParseOptions;
use crate::ebml::element_reader::{ElementChildIterator, ElementIdent, ElementReaderYield}; use crate::ebml::element_reader::{ElementChildIterator, ElementIdent, ElementReaderYield};
use crate::ebml::{EbmlTag, Language, SimpleTag, Tag, TagValue, Target, TargetType}; use crate::ebml::{Language, MatroskaTag, SimpleTag, Tag, TagValue, Target, TargetType};
use crate::error::Result; use crate::error::Result;
use crate::macros::decode_err; use crate::macros::decode_err;
@ -9,7 +9,7 @@ use std::io::{Read, Seek};
pub(super) fn read_from<R>( pub(super) fn read_from<R>(
children_reader: &mut ElementChildIterator<'_, R>, children_reader: &mut ElementChildIterator<'_, R>,
_parse_options: ParseOptions, _parse_options: ParseOptions,
tag: &mut EbmlTag, tag: &mut MatroskaTag,
) -> Result<()> ) -> Result<()>
where where
R: Read + Seek, R: Read + Seek,

View file

@ -2,7 +2,7 @@
//! //!
//! NOTE: We can **ONLY** convert `SimpleTags` that come from a target with **NO** uids //! NOTE: We can **ONLY** convert `SimpleTags` that come from a target with **NO** uids
use super::{EbmlTag, Language, SimpleTag, TargetType, TOMBSTONE_SIMPLE_TAG}; use super::{Language, MatroskaTag, SimpleTag, TargetType, TOMBSTONE_SIMPLE_TAG};
use crate::tag::items::Lang; use crate::tag::items::Lang;
use crate::tag::{ItemKey, Tag, TagItem, TagType}; use crate::tag::{ItemKey, Tag, TagItem, TagType};
@ -135,8 +135,8 @@ matroska_mapping_tables!(
const TAG_RETAINED: bool = true; const TAG_RETAINED: bool = true;
const TAG_CONSUMED: bool = false; const TAG_CONSUMED: bool = false;
pub(super) fn split_tag(mut ebml_tag: EbmlTag) -> (EbmlTag, Tag) { pub(super) fn split_tag(mut ebml_tag: MatroskaTag) -> (MatroskaTag, Tag) {
let mut tag = Tag::new(TagType::Ebml); let mut tag = Tag::new(TagType::Matroska);
// TODO: Pictures, can they be handled in a generic way? // TODO: Pictures, can they be handled in a generic way?
// What about the uid and referral? // What about the uid and referral?

View file

@ -31,7 +31,7 @@ macro_rules! impl_accessor {
paste::paste! { paste::paste! {
$( $(
fn $method(&self) -> Option<Cow<'_, str>> { fn $method(&self) -> Option<Cow<'_, str>> {
self.get_str(EbmlTagKey(TargetType::$target, Cow::Borrowed($name))) self.get_str(MatroskaTagKey(TargetType::$target, Cow::Borrowed($name)))
} }
fn [<set_ $method>](&mut self, value: String) { fn [<set_ $method>](&mut self, value: String) {
@ -57,18 +57,18 @@ macro_rules! impl_accessor {
/// * [`Target`] /// * [`Target`]
/// * [`AttachedFile`] /// * [`AttachedFile`]
#[derive(Default, Debug, PartialEq, Eq, Clone)] #[derive(Default, Debug, PartialEq, Eq, Clone)]
#[tag(description = "An `EBML` \"tag\"", supported_formats(Ebml))] #[tag(description = "A Matroska/WebM \"tag\"", supported_formats(Ebml))]
pub struct EbmlTag { pub struct MatroskaTag {
pub(crate) tags: Vec<Tag<'static>>, pub(crate) tags: Vec<Tag<'static>>,
pub(crate) attached_files: Vec<AttachedFile<'static>>, pub(crate) attached_files: Vec<AttachedFile<'static>>,
} }
// TODO // TODO
#[allow(missing_docs)] #[allow(missing_docs)]
pub struct EbmlTagKey<'a>(TargetType, Cow<'a, str>); pub struct MatroskaTagKey<'a>(TargetType, Cow<'a, str>);
impl EbmlTag { impl MatroskaTag {
fn get(&self, key: EbmlTagKey<'_>) -> Option<&SimpleTag<'_>> { fn get(&self, key: MatroskaTagKey<'_>) -> Option<&SimpleTag<'_>> {
fn tag_matches_target(tag: &Tag<'_>, target_type: TargetType) -> bool { fn tag_matches_target(tag: &Tag<'_>, target_type: TargetType) -> bool {
let Some(target) = &tag.target else { let Some(target) = &tag.target else {
// An empty target is implicitly `Album` // An empty target is implicitly `Album`
@ -78,7 +78,7 @@ impl EbmlTag {
target.is_candidate_for_type(target_type) target.is_candidate_for_type(target_type)
} }
let EbmlTagKey(target, key) = key; let MatroskaTagKey(target, key) = key;
let applicable_tags = self let applicable_tags = self
.tags .tags
@ -98,7 +98,7 @@ impl EbmlTag {
None None
} }
fn get_str(&self, key: EbmlTagKey<'_>) -> Option<Cow<'_, str>> { fn get_str(&self, key: MatroskaTagKey<'_>) -> Option<Cow<'_, str>> {
let simple_tag = self.get(key)?; let simple_tag = self.get(key)?;
simple_tag.get_str().map(Cow::from) simple_tag.get_str().map(Cow::from)
} }
@ -131,11 +131,11 @@ impl EbmlTag {
/// # Examples /// # Examples
/// ///
/// ```rust,no_run /// ```rust,no_run
/// use lofty::ebml::EbmlTag; /// use lofty::ebml::MatroskaTag;
/// use lofty::picture::Picture; /// use lofty::picture::Picture;
/// ///
/// # fn main() -> lofty::error::Result<()> { /// # fn main() -> lofty::error::Result<()> {
/// let mut tag = EbmlTag::default(); /// let mut tag = MatroskaTag::default();
/// ///
/// let mut picture = std::fs::read("something.png")?; /// let mut picture = std::fs::read("something.png")?;
/// let mut picture2 = std::fs::read("something_else.png")?; /// let mut picture2 = std::fs::read("something_else.png")?;
@ -153,14 +153,14 @@ impl EbmlTag {
/// Inserts a new [`Picture`] /// Inserts a new [`Picture`]
/// ///
/// Note: See [`EbmlTag::insert_attached_file`] /// Note: See [`MatroskaTag::insert_attached_file`]
/// ///
/// ```rust,no_run /// ```rust,no_run
/// use lofty::ebml::EbmlTag; /// use lofty::ebml::MatroskaTag;
/// use lofty::picture::Picture; /// use lofty::picture::Picture;
/// ///
/// # fn main() -> lofty::error::Result<()> { /// # fn main() -> lofty::error::Result<()> {
/// let mut tag = EbmlTag::default(); /// let mut tag = MatroskaTag::default();
/// ///
/// let mut picture_file = std::fs::read("something.png")?; /// let mut picture_file = std::fs::read("something.png")?;
/// tag.insert_picture(Picture::from_reader(&mut &picture_file[..])?); /// tag.insert_picture(Picture::from_reader(&mut &picture_file[..])?);
@ -219,7 +219,7 @@ impl EbmlTag {
} }
} }
impl Accessor for EbmlTag { impl Accessor for MatroskaTag {
impl_accessor!( impl_accessor!(
artist => (Track, "ARTIST"), artist => (Track, "ARTIST"),
title => (Track, "TITLE"), title => (Track, "TITLE"),
@ -267,13 +267,13 @@ impl Accessor for EbmlTag {
} }
} }
impl TagExt for EbmlTag { impl TagExt for MatroskaTag {
type Err = LoftyError; type Err = LoftyError;
type RefKey<'a> = EbmlTagKey<'a>; type RefKey<'a> = MatroskaTagKey<'a>;
#[inline] #[inline]
fn tag_type(&self) -> TagType { fn tag_type(&self) -> TagType {
TagType::Ebml TagType::Matroska
} }
fn len(&self) -> usize { fn len(&self) -> usize {
@ -281,7 +281,7 @@ impl TagExt for EbmlTag {
} }
fn contains<'a>(&'a self, key: Self::RefKey<'a>) -> bool { fn contains<'a>(&'a self, key: Self::RefKey<'a>) -> bool {
let EbmlTagKey(target_type, key) = key; let MatroskaTagKey(target_type, key) = key;
self.tags.iter().any(|tag| { self.tags.iter().any(|tag| {
if let Some(target) = &tag.target { if let Some(target) = &tag.target {
return target.target_type == target_type return target.target_type == target_type
@ -338,23 +338,23 @@ impl TagExt for EbmlTag {
#[doc(hidden)] #[doc(hidden)]
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct SplitTagRemainder(EbmlTag); pub struct SplitTagRemainder(MatroskaTag);
impl From<SplitTagRemainder> for EbmlTag { impl From<SplitTagRemainder> for MatroskaTag {
fn from(from: SplitTagRemainder) -> Self { fn from(from: SplitTagRemainder) -> Self {
from.0 from.0
} }
} }
impl Deref for SplitTagRemainder { impl Deref for SplitTagRemainder {
type Target = EbmlTag; type Target = MatroskaTag;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl SplitTag for EbmlTag { impl SplitTag for MatroskaTag {
type Remainder = SplitTagRemainder; type Remainder = SplitTagRemainder;
fn split_tag(mut self) -> (Self::Remainder, crate::tag::Tag) { fn split_tag(mut self) -> (Self::Remainder, crate::tag::Tag) {
@ -364,20 +364,20 @@ impl SplitTag for EbmlTag {
} }
impl MergeTag for SplitTagRemainder { impl MergeTag for SplitTagRemainder {
type Merged = EbmlTag; type Merged = MatroskaTag;
fn merge_tag(self, _tag: crate::tag::Tag) -> Self::Merged { fn merge_tag(self, _tag: crate::tag::Tag) -> Self::Merged {
todo!() todo!()
} }
} }
impl From<EbmlTag> for crate::tag::Tag { impl From<MatroskaTag> for crate::tag::Tag {
fn from(input: EbmlTag) -> Self { fn from(input: MatroskaTag) -> Self {
input.split_tag().1 input.split_tag().1
} }
} }
impl From<crate::tag::Tag> for EbmlTag { impl From<crate::tag::Tag> for MatroskaTag {
fn from(input: crate::tag::Tag) -> Self { fn from(input: crate::tag::Tag) -> Self {
SplitTagRemainder::default().merge_tag(input) SplitTagRemainder::default().merge_tag(input)
} }

View file

@ -35,6 +35,7 @@ impl FileType {
/// | `Ape` , `Mpc`, `WavPack` | `Ape` | /// | `Ape` , `Mpc`, `WavPack` | `Ape` |
/// | `Flac`, `Opus`, `Vorbis`, `Speex` | `VorbisComments` | /// | `Flac`, `Opus`, `Vorbis`, `Speex` | `VorbisComments` |
/// | `Mp4` | `Mp4Ilst` | /// | `Mp4` | `Mp4Ilst` |
/// | `Ebml` | `Matroska` |
/// ///
/// # Panics /// # Panics
/// ///
@ -53,7 +54,7 @@ impl FileType {
match self { match self {
FileType::Aac | FileType::Aiff | FileType::Mpeg | FileType::Wav => TagType::Id3v2, FileType::Aac | FileType::Aiff | FileType::Mpeg | FileType::Wav => TagType::Id3v2,
FileType::Ape | FileType::Mpc | FileType::WavPack => TagType::Ape, FileType::Ape | FileType::Mpc | FileType::WavPack => TagType::Ape,
FileType::Ebml => TagType::Ebml, FileType::Ebml => TagType::Matroska,
FileType::Flac | FileType::Opus | FileType::Vorbis | FileType::Speex => { FileType::Flac | FileType::Opus | FileType::Vorbis | FileType::Speex => {
TagType::VorbisComments TagType::VorbisComments
}, },
@ -92,7 +93,7 @@ impl FileType {
match tag_type { match tag_type {
TagType::Ape => crate::ape::ApeTag::SUPPORTED_FORMATS.contains(self), TagType::Ape => crate::ape::ApeTag::SUPPORTED_FORMATS.contains(self),
TagType::Ebml => crate::ebml::EbmlTag::SUPPORTED_FORMATS.contains(self), TagType::Matroska => crate::ebml::MatroskaTag::SUPPORTED_FORMATS.contains(self),
TagType::Id3v1 => crate::id3::v1::Id3v1Tag::SUPPORTED_FORMATS.contains(self), TagType::Id3v1 => crate::id3::v1::Id3v1Tag::SUPPORTED_FORMATS.contains(self),
TagType::Id3v2 => crate::id3::v2::Id3v2Tag::SUPPORTED_FORMATS.contains(self), TagType::Id3v2 => crate::id3::v2::Id3v2Tag::SUPPORTED_FORMATS.contains(self),
TagType::Mp4Ilst => crate::mp4::Ilst::SUPPORTED_FORMATS.contains(self), TagType::Mp4Ilst => crate::mp4::Ilst::SUPPORTED_FORMATS.contains(self),

View file

@ -1,3 +1,4 @@
use crate::ebml::MatroskaTag;
use crate::id3::v2::Id3v2Tag; use crate::id3::v2::Id3v2Tag;
use crate::mp4::Ilst; use crate::mp4::Ilst;
@ -5,6 +6,7 @@ use crate::mp4::Ilst;
pub(crate) enum CompanionTag { pub(crate) enum CompanionTag {
Id3v2(Id3v2Tag), Id3v2(Id3v2Tag),
Ilst(Ilst), Ilst(Ilst),
Matroska(MatroskaTag),
} }
impl CompanionTag { impl CompanionTag {
@ -21,4 +23,11 @@ impl CompanionTag {
_ => None, _ => None,
} }
} }
pub(crate) fn matroska(self) -> Option<MatroskaTag> {
match self {
CompanionTag::Matroska(tag) => Some(tag),
_ => None,
}
}
} }

View file

@ -902,7 +902,7 @@ impl TagItem {
return VALID_ITEMKEYS.contains(&self.item_key); return VALID_ITEMKEYS.contains(&self.item_key);
} }
if tag_type == TagType::Ebml { if tag_type == TagType::Matroska {
use crate::ebml::tag::SUPPORTED_ITEMKEYS; use crate::ebml::tag::SUPPORTED_ITEMKEYS;
return SUPPORTED_ITEMKEYS.contains(&self.item_key); return SUPPORTED_ITEMKEYS.contains(&self.item_key);

View file

@ -70,7 +70,7 @@ pub trait MergeTag: private::Sealed {
// https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed // https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed
mod private { mod private {
use crate::ape::ApeTag; use crate::ape::ApeTag;
use crate::ebml::EbmlTag; use crate::ebml::MatroskaTag;
use crate::id3::v1::Id3v1Tag; use crate::id3::v1::Id3v1Tag;
use crate::id3::v2::Id3v2Tag; use crate::id3::v2::Id3v2Tag;
use crate::iff::aiff::AiffTextChunks; use crate::iff::aiff::AiffTextChunks;
@ -86,7 +86,7 @@ mod private {
impl Sealed for ApeTag {} impl Sealed for ApeTag {}
impl Sealed for crate::ape::tag::SplitTagRemainder {} impl Sealed for crate::ape::tag::SplitTagRemainder {}
impl Sealed for EbmlTag {} impl Sealed for MatroskaTag {}
impl Sealed for crate::ebml::tag::SplitTagRemainder {} impl Sealed for crate::ebml::tag::SplitTagRemainder {}
impl Sealed for Id3v1Tag {} impl Sealed for Id3v1Tag {}

View file

@ -153,7 +153,7 @@ pub trait TagExt: Accessor + Into<Tag> + Sized + private::Sealed {
// https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed // https://rust-lang.github.io/api-guidelines/future-proofing.html#c-sealed
mod private { mod private {
use crate::ape::ApeTag; use crate::ape::ApeTag;
use crate::ebml::EbmlTag; use crate::ebml::MatroskaTag;
use crate::id3::v1::Id3v1Tag; use crate::id3::v1::Id3v1Tag;
use crate::id3::v2::Id3v2Tag; use crate::id3::v2::Id3v2Tag;
use crate::iff::aiff::AiffTextChunks; use crate::iff::aiff::AiffTextChunks;
@ -166,7 +166,7 @@ mod private {
impl Sealed for AiffTextChunks {} impl Sealed for AiffTextChunks {}
impl Sealed for ApeTag {} impl Sealed for ApeTag {}
impl Sealed for EbmlTag {} impl Sealed for MatroskaTag {}
impl Sealed for Id3v1Tag {} impl Sealed for Id3v1Tag {}
impl Sealed for Id3v2Tag {} impl Sealed for Id3v2Tag {}
impl Sealed for Ilst {} impl Sealed for Ilst {}

View file

@ -15,8 +15,8 @@ use std::path::Path;
pub enum TagType { pub enum TagType {
/// This covers both APEv1 and APEv2 as it doesn't matter much /// This covers both APEv1 and APEv2 as it doesn't matter much
Ape, Ape,
/// Represents an EBML tag element /// Represents a `\Segment\Tags` element in Matroska/WebM
Ebml, Matroska,
/// Represents an ID3v1 tag /// Represents an ID3v1 tag
Id3v1, Id3v1,
/// This covers all ID3v2 versions since they all get upgraded to ID3v2.4 /// This covers all ID3v2 versions since they all get upgraded to ID3v2.4

View file

@ -34,6 +34,7 @@ where
FileType::Aac => aac::write::write_to(file, tag, write_options), FileType::Aac => aac::write::write_to(file, tag, write_options),
FileType::Aiff => iff::aiff::write::write_to(file, tag, write_options), FileType::Aiff => iff::aiff::write::write_to(file, tag, write_options),
FileType::Ape => ape::write::write_to(file, tag, write_options), FileType::Ape => ape::write::write_to(file, tag, write_options),
FileType::Ebml => todo!("write EBML tags"),
FileType::Flac => flac::write::write_to(file, tag, write_options), FileType::Flac => flac::write::write_to(file, tag, write_options),
FileType::Opus | FileType::Speex | FileType::Vorbis => { FileType::Opus | FileType::Speex | FileType::Vorbis => {
crate::ogg::write::write_to(file, tag, file_type, write_options) crate::ogg::write::write_to(file, tag, file_type, write_options)
@ -98,6 +99,9 @@ pub(crate) fn dump_tag<W: Write>(
} }
} }
.dump_to(writer, write_options), .dump_to(writer, write_options),
TagType::Matroska => {
todo!("Dump EBML tags")
},
_ => Ok(()), _ => Ok(()),
} }
} }

View file

@ -36,7 +36,7 @@ fn write() {
assert_eq!(tagged_file.file_type(), FileType::Ebml); assert_eq!(tagged_file.file_type(), FileType::Ebml);
// Tags // Tags
crate::set_artist!(tagged_file, tag_mut, TagType::Ebml, "Foo artist", 1 => file, "Bar artist"); crate::set_artist!(tagged_file, tag_mut, TagType::Matroska, "Foo artist", 1 => file, "Bar artist");
// Now reread the file // Now reread the file
file.rewind().unwrap(); file.rewind().unwrap();
@ -48,12 +48,15 @@ fn write() {
.read() .read()
.unwrap(); .unwrap();
crate::set_artist!(tagged_file, tag_mut, TagType::Ebml, "Bar artist", 1 => file, "Foo artist"); crate::set_artist!(tagged_file, tag_mut, TagType::Matroska, "Bar artist", 1 => file, "Foo artist");
} }
#[test_log::test] #[test_log::test]
fn remove() { fn remove() {
crate::remove_tag!("tests/files/assets/minimal/full_test.mka", TagType::Ebml); crate::remove_tag!(
"tests/files/assets/minimal/full_test.mka",
TagType::Matroska
);
} }
#[test_log::test] #[test_log::test]

View file

@ -51,7 +51,7 @@ pub(crate) fn init_write_lookup(
.write_to(file, write_options) .write_to(file, write_options)
}); });
insert!(map, Ebml, { todo!() }); insert!(map, Matroska, { todo!("Generated Matroska tag writer") });
insert!(map, Id3v1, { insert!(map, Id3v1, {
Into::<lofty::id3::v1::tag::Id3v1TagRef<'_>>::into(tag).write_to(file, write_options) Into::<lofty::id3::v1::tag::Id3v1TagRef<'_>>::into(tag).write_to(file, write_options)