Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Tags in rbx_types, rbx_binary, rbx_xml, and rbx_dom_lua #199

Merged
merged 38 commits into from
Oct 9, 2021
Merged

Implement Tags in rbx_types, rbx_binary, rbx_xml, and rbx_dom_lua #199

merged 38 commits into from
Oct 9, 2021

Conversation

kennethloeffler
Copy link
Member

There is currently no special representation for CollectionService tags in rbx-dom. To address this, this PR adds the new type Tags to rbx_types. It is a wrapper over Vec<String> that provides conversions between byte arrays and Tags. It changes the type of Instance.Tags from BinaryString to Tags and makes rbx_binary and rbx_xml write to and read from this property using Tags. This makes it possible for consumers like Rojo to expose a more friendly interface for CollectionService tags rather than requiring users to provide a base64 string.


fn read_xml<R: Read>(reader: &mut XmlEventReader<R>) -> Result<Tags, DecodeError> {
let value = reader.read_base64_characters()?;
Ok(value.try_into().unwrap_or_default())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to use map_err and the question mark operator here but couldn't get it to compile :/

pub fn as_vec(&self) -> &Vec<String> {
&self.tag_list
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You never want to return &Vec<T> in Rust. How about fn as_slice(&self) -> &[String]?

Comment on lines 37 to 35
impl From<Vec<&str>> for Tags {
fn from(tag_list: Vec<&str>) -> Tags {
Self {
tag_list: tag_list
.iter()
.map(|tag_name| String::from_str(tag_name).unwrap())
.collect(),
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we need this; users can convert to Vec<String> first themselves:

tags.into_iter().map(String::from).collect()

If we wanted this, we would be better off implementing FromIterator to get a bunch of containers for free.

Comment on lines 48 to 42
impl From<&Tags> for Vec<u8> {
fn from(tags: &Tags) -> Vec<u8> {
let tag_list = &tags.tag_list;
let mut buf: Vec<u8> = Vec::with_capacity(tag_list.capacity() + tag_list.len());

for tag_name in tag_list.as_slice() {
buf.extend_from_slice(tag_name.as_bytes());
buf.extend_from_slice(b"\0")
}

buf
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels pretty weird to have as part of From. If we want this method, we should write an explicit method for it.

This can also be a one-liner, which makes me think we should consider writing that inline when we need it in rbx_xml and rbx_binary:

fn encode_tags(tags: &[&str]) -> Vec<u8> {
    tags.join("\u{0000}").into_bytes()
}

Comment on lines 62 to 41
impl TryFrom<Vec<u8>> for Tags {
type Error = FromUtf8Error;

fn try_from(buf: Vec<u8>) -> Result<Tags, FromUtf8Error> {
let len = buf.len();
let mut tag_list = Vec::new();
let mut count = 0;

while count < len {
let tag_name: Vec<u8> = buf
.iter()
.skip(count)
.copied()
.take_while(|element| {
count += 1;
*element != 0
})
.collect();

tag_list.push(String::from_utf8(tag_name)?);
}

Ok(Self { tag_list })
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use slice::split and save a lot of effort here.

Similar to the From implementation, this should probably be its own method instead of a generic trait implementation.

serde(transparent)
)]
pub struct Tags {
tag_list: Vec<String>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could have a general name like members.

@kennethloeffler
Copy link
Member Author

rbx_xml has no great way to discern if a BinaryString in the file is in fact a Tags or not. It should start dealing with property descriptors in a way that's closer to rbx_binary (which handles this just fine). This work is blocked until then.

@kennethloeffler kennethloeffler marked this pull request as draft July 17, 2021 04:54
@kennethloeffler
Copy link
Member Author

Never mind on that - we can add a conversion from BinaryString to Tags, which makes the Tags XML reader/writer totally unnecessary. However, rbx_xml could probably still use a refactor at some point, especially with respect to its tests: in particular, I think it would be good to start using the files from the test-files submodule.

@kennethloeffler kennethloeffler marked this pull request as ready for review July 17, 2021 20:26
@kennethloeffler
Copy link
Member Author

Simply adding the conversion wasn't enough for rbx_xml to write Tags as BinaryString. This change fixes that, but it's ugly :(

@kennethloeffler kennethloeffler changed the title Implement Tags in rbx_types, rbx_binary, and rbx_xml Implement Tags in rbx_types, rbx_binary, rbx_xml, and rbx_dom_lua Aug 26, 2021
@LPGhatguy LPGhatguy merged commit e9f3dfe into rojo-rbx:master Oct 9, 2021
@kennethloeffler kennethloeffler deleted the tags branch October 9, 2021 19:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants